This chapter is all about control structures, like if
statements, switch blocks, loops and breaks. Specifically, we’ll look at:
if
statements, ternary expressions and switchesdo
, while
and for
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 favoured 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.
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 which 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 realise 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.
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, March are in the first quarter, April, May, 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 code above 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
_
=>
"unknown quarter"
}))
}
do
, while
and for
Scala and Java share the same syntax for do and while loops. For example, this 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: initialise, check and update.
There is no direct analog in Scala. You’ve seen an alternative — using the while loop to initialise 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 a iteration in Scala can be used as a generator.
In this case, 0 to 9
is the generator. Zero is an Int
literal and the class Int
has a method called to
that takes an Int
and returns a range of numbers which 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:
(
0
to
10
).
foreach
(
println
(
_
))
// scala
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 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.
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 curl. 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. That 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.)