© Toby Weston 2018

Toby Weston, Scala for Java Developers, https://doi.org/10.1007/978-1-4842-3108-1_12

12. Control Structures

Toby Weston

(1)London, UK

This chapter is all about control structures, like if statements, switch blocks, loops, and breaks. Specifically, we’ll look at the following:

  • Conditionals like if statements, ternary expressions, and switches.

  • Looping structures: do, while and for

  • Breaking control flow.

  • Exceptions, briefly.

Conditionals

Ifs and Ternaries

Conditionals are straightforward.

  // java
  if (age > 55) {
      retire();
  } else {
      carryOnWorking();
  }

An if in Java looks exactly the same in Scala.

  // scala
  if (age > 55) {
    retire()
  } else {
    carryOnWorking()
  }

You’ll often find Scala developers dropping the braces for simple if blocks. For example:

  if (age > 55)
    retire()
  else
    carryOnWorking()

…or even pulling it all onto one line.

  if (age > 55) retire() else carryOnWorking()

This style is favored because if/else is actually an expression in Scala and not a statement, and the more concise syntax makes it look more like an expression. What’s the difference? Well, an expression returns a value whereas a statement carries out an action.

Expressions vs. Statements

An expression returns a value whereas a statement carries out an action. Statements by their nature often have side effects whereas expressions are less likely to do so.

For example, let’s add a creation method to our Customer class that will create either a DiscountedCustomer or a regular Customer based on how long they’ve been a customer.

  // java
  public static Customer create(String name, String address,
          Integer yearsOfCustom) {
      if (yearsOfCustom > 2) {
          return new DiscountedCustomer(name, address);
      } else {
          return new Customer(name, address);
      }
  }

In Java, we’re forced to return the new Customer from the method. The conditions are statements, things that execute, not expressions that return values. We could do it longhand and create a variable, set then return it, but the point is the same; the statements here have to cause a side effect.

  public static Customer create(String name, String address,
          Integer yearsOfCustom) {
      Customer customer = null;
      if (yearsOfCustom > 2) {
          customer = new DiscountedCustomer(name, address);
      } else {
          customer = new Customer(name, address);
      }
      return customer;
  }

Because conditionals in Scala are expressions, you don’t need to jump through these hoops. In the Scala equivalent, we can just create the if and both branches will return a customer. As the entire expression is the last statement in the method, it is what will be returned from the method.

  // scala
  object Customer {
    def create(name: String, address: String, yearsOfCustom: Int) = {
      if (yearsOfCustom > 2)
        new DiscountedCustomer(name, address)
      else
        new Customer(name, address)
    }
  }

Longhand, we can assign the result of the if (remember it’s an expression not a statement) to a val and then return the value on the last line.

  object Customer {
    def create(name: String, address: String, yearsOfCustom: Int) = {
      val customer = if (yearsOfCustom > 2)
        new DiscountedCustomer(name, address)
      else
        new Customer(name, address)
      customer
    }
  }

Another trivial example might be something like this:

  val tall = if (height > 190) "tall" else "not tall"     // scala

You may have noticed this behaves like a ternary expression in Java.

  String tall = height > 190 ? "tall" : "not tall";       // java

So, ternaries are expressions in Java but if statements are not. Scala has no conditional operator (?:) because a regular Scala if is an expression; it’s equivalent to Java’s conditional operator. In fact, the bytecode generated for an if uses a ternary.

You don’t have to use an if in Scala like a ternary and assign it to anything, but it’s important to realize that it is an expression and has a value. In fact, everything in Scala is an expression. Even a simple block (denoted with curly braces) will return something.

Switch Statements

There are no switch statements as such in Scala. Scala uses match expressions instead. These look like they’re switching but differ in that the whole thing is an expression and not a statement. So, as we saw with the if, Scala’s switch-like construct has a value. It also uses something called pattern matching, which is a lot more powerful as it allows you to select on more than just equality.

In Java, you might write a switch to work out which quarter a particular month falls in. So, January, February, and March are in the first quarter, April, May, and June in the second, and so on.

  // java
  public class Switch {
      public static void main(String... args) {
          String month = "August";
          String quarter;
          switch (month) {
              case "January":
              case "February":
              case "March":
                  quarter = "1st quarter";
                  break;
              case "April":
              case "May":
              case "June":
                  quarter = "2nd quarter";
                  break;
              case "July":
              case "August":
              case "September":
                  quarter = "3rd quarter";
                  break;
              case "October":
              case "November":
              case "December":
                  quarter = "4th quarter";
                  break;
              default:
                  quarter = "unknown quarter";
                  break;
          }
          System.out.println(quarter);
      }
  }

The break is required to stop the statement execution falling through. When Java selects a case, it has to have a side effect to be useful. In this case, it assigns a value to a variable.

In Scala, we’d start with something like this:

  // scala
  object BrokenSwitch extends App {
    val month = "August"
    var quarter = "???"
    month match {
    case "January"   =>
    case "February"  =>
    case "March"     => quarter = "1st quarter"
    case "April"     =>
    case "May"       =>
    case "June"      => quarter = "2nd quarter"
    case "July"      =>
    case "August"    =>
    case "September" => quarter = "3rd quarter"
    case "October"   =>
    case "November"  =>
    case "December"  => quarter = "4th quarter"
    case _           => quarter = "unknown quarter"
    }
    println(month + " is " + quarter)
  }

The above is a direct syntactic translation. However, Scala doesn’t support the break keyword so we have to leave that out. Rather than switch we use match and we’re saying "does the month match any of these case clauses?"

Rather than the colon, we use => and the underscore at the bottom is the catch-all, the same as default in Java. Underscore is often used in Scala to mean an unknown value.

So, although this is a direct translation, when we run it, something has gone wrong. The result hasn’t been set.

The output says:

  August is ???

Unlike Java, if a case matches, the break is implicit—there is no fall-through to the next case. So, we’ll have to add some code to the empty blocks.

  // scala
  object Switch extends App {
    val month = "August"
    var quarter = "???"
    month match {
      case "January"   => quarter = "1st quarter"
      case "February"  => quarter = "1st quarter"
      case "March"     => quarter = "1st quarter"
      case "April"     => quarter = "2nd quarter"
      case "May"       => quarter = "2nd quarter"
      case "June"      => quarter = "2nd quarter"
      case "July"      => quarter = "3nd quarter"
      case "August"    => quarter = "3rd quarter"
      case "September" => quarter = "3rd quarter"
      case "October"   => quarter = "4th quarter"
      case "November"  => quarter = "4th quarter"
      case "December"  => quarter = "4th quarter"
      case _           => quarter = "unknown quarter"
    }
    println(month + " is " + quarter)
  }

This time it works but we’ve duplicated a fair bit.

To remove some of the duplication, we can combine January, February, and March onto one line, separating them with an or. This means that the month can match either January, February, or March. In all of these cases, what follows the => will be executed.

  case "January" | "February" | "March" => quarter = "1st quarter"

Doing this for the rest of the cases would give us the following:

  // scala
    object SwitchWithLessDuplication extends App {
    val month = "August"
    var quarter = "???"
    month match {
      case "January" | "February" | "March"    => quarter = "1st quarter"
      case "April" | "May" | "June"            => quarter = "2nd quarter"
      case "July" | "August" | "September"     => quarter = "3rd quarter"
      case "October" | "November" | "December" => quarter = "4th quarter"
      case _ => quarter = "unknown quarter"
    }
    println(month + " is " + quarter)
  }

We’ve condensed the preceding code by writing expressions within the case clauses themselves. This becomes more powerful when we think of these case clauses as patterns that we can use to build up more and more expressive conditions for the match.

Java can only switch on primitives, enums, and, from Java 7, string values. Thanks to pattern matching, Scala can match on almost anything, including objects. We’ll look more at pattern matching in Part III.

The other thing to note is that Scala’s version of the switch is an expression. We’re not forced to work with side effects and can drop the temporary variable and return a String to represent the quarter the month falls into. We can then change the quarter variable from being a var to a val.

  // scala
  object SwitchExpression extends App {
    val month = "August"
    val quarter = month match {
      case "January" | "February" | "March"    => "1st quarter"
      case "April" | "May" | "June"            => "2nd quarter"
      case "July" | "August" | "September"     => "3rd quarter"
      case "October" | "November" | "December" => "4th quarter"
      case _ => "unknown quarter"
    }
    println(month + " is " + quarter)
  }

We could even do it in-line. We just need to add some parentheses around the match, like this:

  // scala
  object SwitchExpression extends App {
    val month = "August"
    println(month + " is " + (month match {
      case "January" | "February" | "March"    => "1st quarter"
      case "April" | "May" | "June"            => "2nd quarter"
      case "July" | "August" | "September"     => "3rd quarter"
      case "October" | "November" | "December" => "4th quarter"
      case _
    }))
  }

Looping Structures: do, while and for

Scala and Java share the same syntax for do and while loops . For example, the following code uses a do and a while to print the numbers zero to nine:

  // java
  int i = 0;
  do {
      System.out.println(i);
      i++;
  } while (i < 10);

The Scala version would look like this (there is no ++ incrementer so we use += instead):

  // scala
  var i: Int = 0
  do {
    println(i)
    i += 1
  } while (i < 10)

And while loops are the same.

  // java
  int i = 0;
  while (i < 10) {
      System.out.println(i);
      i++;
  }


  // scala
  var i: Int = 0
  while (i < 10) {
    println(i)
    i += 1
  }

Things get more interesting when we look at for loops . Scala doesn’t have for loops like Java does; it has what’s referred to as the “generator-based for loop” and the related “for comprehension” instead. To all intents and purposes, these can be used like Java’s for loop construct, so for the most part you won’t have to worry about the technical differences.

Java’s for loop controls the iteration in three stages, as shown in Figure 12-1: initialize, check, and update.

A456960_1_En_12_Fig1_HTML.jpg
Figure 12-1 The typical for loop iteration stages

There is no direct analog in Scala. You’ve seen an alternative—using the while loop to initialize a variable, check a condition, then update the variable—but you can also use a generator-based for loop in Scala. So, the following for in Java:

  // java
  for (int i = 0; i < 10; i++) {
      System.out.println(i);
  }

…would look like this using a generator-based for loop in Scala:

  // scala
  for (i <- 0 to 9) {
    println(i)
  }

The i variable has been created for us and is assigned a value on each iteration. The arrow indicates that what follows is a generator. A generator is something that can feed values into the loop. The whole thing is a lot like Java’s enhanced for loops where anything that is Iterable can be used. In the same way, anything that can generate an iteration in Scala can be used as a generator.

In this case, 0 to 9 is the generator. 0 is an Int literal and the class Int has a method called to that takes an Int and returns a range of numbers that can be enumerated. The example uses the infix shorthand, but we could have written it longhand like this:

  for (i <- 0.to(9)) {
    println(i)
  }

It’s very similar to the following enhanced for loop in Java, using a list of numbers:

  // java
  List<Integer> numbers = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
  for (Integer i : numbers) {
      System.out.println(i);
  }

…which itself could be rewritten in Java as the following:

  numbers.forEach(i -> System.out.println(i));             // java

…or as a method reference.

  numbers.forEach(System.out::println);                    // java

Unsurprisingly, Scala has a foreach method of its own.

  (0 to 9).foreach(i => println(i))                        // scala

We use the to method again to create a sequence of numbers. This sequence has the foreach method, which we call, passing in a lambda. The lambda function takes an Int and returns Unit.

We can even use Scala’s shorthand like we did with Java’s method reference as follows:

  (0 to 10).foreach(println(_))                            // scala

Breaking Control Flow (break and continue)

Scala has no break or continue statements, and generally discourages you from breaking out of loops . However, you can use a library method to achieve the same thing. In Java, you might write something like this to break out of a loop early:

  // java
  for (int i = 0; i < 100; i++) {
      System.out.println(i);
      if (i == 10)
          break;
  }

In Scala, you need to import a Scala library class called Breaks. You can then enclose the code to break out from in a “breakable” block and call the break method to break out. It’s implemented by throwing an exception and catching it.

  // scala
  import scala.util.control.Breaks._
  breakable {                             // breakable block
    for (i <- 0 to 100) {
      println(i)
      if (i == 10)
        break()                           // break out of the loop
      }
  }

Exceptions

Exceptions in Scala are handled in the same way as Java. They have all the same schematics in terms of interrupting control flow and aborting the program if not dealt with.

Exceptions in Scala extend java.lang.Throwable like their Java counterparts but Scala has no concept of checked exceptions. All checked exceptions thrown from existing Java libraries get converted to RuntimeExceptions. Any exceptions you throw don’t need to be dealt with to keep the compiler happy; all exceptions in Scala are runtime exceptions (see Figure 12-2).

A456960_1_En_12_Fig2_HTML.jpg
Figure 12-2 The Java exception hierarchy. Scala doesn’t use checked exceptions.

Catching exceptions uses pattern matching like we saw earlier with match expressions. In Java you might do something like this to get the contents of a web page:

  // java
  try {
      URL url = new URL("http://baddotrobot.com");
      BufferedReader reader = new BufferedReader(
          new InputStreamReader(url.openStream()));
      try {
          String line;
          while ((line = reader.readLine()) != null)
              System.out.println(line);
      } finally {
          reader.close();
      }
  } catch (MalformedURLException e) {
      System.out.println("Bad URL");
  } catch (IOException e) {
      System.out.println("Problem reading data: " + e.getMessage());
  }

We start with a URL to attempt to download. This can throw a MalformedURLException. As it’s a checked exception, we’re forced to deal with it. We then create a Reader and open a stream from the URL ready for reading. This can throw another exception, so we’re forced to deal with that too.

When we start reading, the readLine method can also throw an exception but that’s handled by the existing catch. To make sure we clean up properly in the event of an exception here, we close the reader in a finally block.

If we want to use Java 7’s try-with-resources construct, we can avoid the finally clause. The try-with-resources syntax will automatically call close on the reader.

  // java
  try {
      URL url = new URL("http://baddotrobot.com");
      try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(url.openStream()))) {
          String line;
          while ((line = reader.readLine()) != null)
              System.out.println(line);
          }
  } catch (MalformedURLException e) {
      System.out.println("Bad URL");
  } catch (IOException e) {
      System.out.println("Problem reading data: " + e.getMessage());
  }

In Scala things look pretty much the same.

  // scala
  try {
    val url = new URL("http://baddotrobot.com")
    val reader = new BufferedReader(new InputStreamReader(url.openStream))
    try {
      var line = reader.readLine
      while (line != null) {
        line = reader.readLine
        println(line)
      }
    } finally {
      reader.close()
    }
  } catch {
    case e: MalformedURLException => println("Bad URL")
    case e: IOException => println(e.getMessage)
  }

We create the URL as before. Although it can throw an exception, we’re not forced to catch it. It’s a Java checked exception but Scala is converting it to a runtime exception.

Although we’re not forced to, we do actually want to deal with the exceptions. So, we use the familiar try and catch statements. In the catch, the exceptions are dealt with using match expressions. We can tweak the pattern if we don’t actually need the exception in the code block by replacing the variable name with an underscore. This means we don’t care about the variable, only the class.

  case _: MalformedURLException => println("Bad URL")

We just need to add the finally block back in. finally is just as it is in Java. There is no try-with-resources equivalent in Scala, although you can write your own method to achieve the same thing. (Hint: With something like the Loan pattern.)

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.51.145