Let’s look at some syntax. We’ll start by creating a variable:
val
language
:
String
=
"Scala"
;
We’ve defined a variable as a String
and assigned to it the value of “Scala”. I say “variable”, but we’ve actually created an immutable value rather than a variable. The val
keyword creates a constant, and language
cannot be modified from this point on. Immutability is a key theme you’ll see again and again in Scala.
If we will want to modify language
later, we can use var
instead of val
. We can then reassign it if we need to.
var
language
:
String
=
"Java"
;
language
=
"Scala"
;
So far, this doesn’t look very different from Java. In the variable definition, the type and variable name are the opposite way round compared to Java, but that’s about it. However, Scala uses type inference heavily, so Scala knows that the var
above is a string, even if we don’t tell it.
val
language
=
"Scala"
;
Similarly, it knows that the expression is finished without needing to tell it explicitly with the semicolon. So we can drop that too.
val
language
=
"Scala"
// no semicolon
You only need to add semicolons when you use multiple expressions on the same line; otherwise things get too complex for the compiler.
Operator precedence is just as you’d expect in Java. In the example below, the multiplication happens before the subtraction.
scala
>
val
age
=
35
scala
>
var
maxHeartRate
=
210
-
age
*
.
5
res0
:
Double
=
191.5
Function and method definitions start with the def
keyword, followed by the signature. The signature looks similar to a Java method signature but with the parameter types the other way round again, and the return type at the end rather than the start.
Let’s create a function to return the minimum of two numbers.
def
min
(
x
:
Int
,
y
:
Int
)
:
Int
=
{
if
(
x
<
y
)
return
x
else
return
y
}
We can test it in the REPL by calling it:
scala
>
min
(
34
,
3
)
res3
:
Int
=
3
scala
>
min
(
10
,
50
)
res4
:
Int
=
10
Note that Scala can’t infer the types of function arguments.
Another trick is that you can drop the return
statement. The last statement in a function will implicitly be the return value.
def
min
(
x
:
Int
,
y
:
Int
)
:
Int
=
{
if
(
x
<
y
)
x
else
y
}
Running it the REPL would show the following:
scala
>
min
(
300
,
43
)
res5
:
Int
=
43
In this example, the else
means the last statement is consistent with a min
function. If I forgot the else
, the last statement would be the same regardless and there would be a bug in our implementation:
def
min
(
x
:
Int
,
y
:
Int
)
:
Int
=
{
if
(
x
<
y
)
x
y
// bug! where's the else?
}
It always returns y
:
scala
>
min
(
10
,
230
)
res6
:
Int
=
230
If you don’t use any return
statements, the return type can usually be inferred.
// the return type can be omitted
def
min
(
x
:
Int
,
y
:
Int
)
=
{
if
(
x
<
y
)
x
else
y
}
Note that it’s the equals sign that says this function returns something. If I write this function on one line, without the return type and just the equals sign, it starts to look like a real expression rather than a function.
def
min
(
x
:
Int
,
y
:
Int
)
=
if
(
x
<
y
)
x
else
y
Be wary, though; if you accidentally drop the equals sign, the function won’t return anything. It’ll be similar to the void
in Java.
def
min
(
x
:
Int
,
y
:
Int
)
{
if
(
x
<
y
)
x
else
y
}
<
console
>:
8
:
warning:
a
pure
expression
does
nothing
in
statement
position
;
you
may
be
omitting
necessary
parentheses
if
(
x
<
y
)
x
else
y
^
<
console
>:
8
:
warning:
a
pure
expression
does
nothing
in
statement
position
;
you
may
be
omitting
necessary
parentheses
if
(
x
<
y
)
x
else
y
^
min
:
(
x:
Int
,
y:
Int
)
Unit
Although this compiles okay, the compiler warns that you may have missed off the equals sign.
One interesting thing to note in Scala is that you can override operators. Arithmetic operators are, in fact, just methods in Scala. As such, you can create your own. Earlier, we saw the integer age
used with a multiplier.
val
age
:
Int
age
*
.
5
The value age
is an integer and there is a method called *
on the integer class. It has the following signature:
def
*(
x
:
Double
)
:
Double
Numbers are objects in Scala, as are literals. So you can call *
directly on a variable or a number.
age
.*(.
5
)
5.
*(
10
)
Using the infix notation, you’re able to drop the dot notation for variables and literals and call:
age
*
.
5
or, as another example:
35
toString
Remember, 35
is an instance of Int
.
Specifically, Scala support for infix notation means that when a method takes zero or one arguments you can drop the dot and parentheses, and if there is more than one argument you can drop the dot.
For example:
35
+
10
"aBCDEFG"
replace
(
"a"
,
"A"
)
It’s optional though; you can use the dot notation if you prefer.
What this means is that you can define your own plus or minus method on your own classes and use it naturally with infix notation. For example, you might have a Passenger
join a Train
.
train
+
passenger
There are not many restrictions on what you can call your functions and methods; you can use any symbol that makes sense to your domain.
Scala comes with its own immutable collection types as well as mutable versions. By default immutability is preferred, so we can create a list with the following:
val
list
=
List
(
"a"
,
"b"
,
"c"
)
And create a map with:
val
map
=
Map
(
1
->
"a"
,
2
->
"b"
)
where the arrow goes from the key to the value. These will be immutable; you won’t be able to add or remove elements.
You can process them in a similar way to Java 8’s forEach
and lambda syntax:
list
.
foreach
(
value
=>
println
(
value
))
// scala
which is equivalent to the following in Java:
list
.
forEach
(
value
->
System
.
out
.
println
(
value
));
// java
Like Java 8’s method reference syntax, you can auto-connect the lambda argument to the method call.
list
.
foreach
(
println
)
// scala
which is roughly equivalent to this Java:
list
.
forEach
(
System
.
out
::
println
);
// java
There are lots of other Scala-esque ways to process collections. We’ll look at these later, but the most common way to iterate is a for
loop written like this:
for
(
value
<-
list
)
println
(
value
)
which reads, “for every value in list, print the value”. You can also do it in reverse:
for
(
value
<-
list
.
reverse
)
println
(
value
)
or you might like to break it across multiple lines:
for
(
value
<-
list
)
{
println
(
value
)
}
I mentioned that you can use any Java class from Scala. For example, let’s say we want to create a Java List
rather than the usual Scala immutable List
.
val
list
=
new
java
.
util
.
ArrayList
[
String
]
All we did was fully qualify the class name (java.util.ArrayList
) and use new
to instantiate it. Notice the square brackets? Scala denotes generics using []
rather than <>
. We also didn’t have to use the parentheses on the constructor, as we had no arguments to pass in.
We can make method calls — for example, adding an element — just as you’d expect:
list
.
add
(
"Hello"
)
or, using infix:
list
add
"World!"
In Java there are two integer types: the primitive (non-object) int
and the Integer
class. Scala has no concept of primitives — everything is an object — so, for example, Scala’s integer type is an Int
. Similarly, you’ll be familiar with the other basic types:
Byte
Short
Int
Long
Char
String
Float
Double
Boolean
Although Scala has its own richer types, typically they just wrap the Java types. When working with these basic types, nine times out of ten you won’t need to worry if you’re using Scala or Java types. Things are pretty seamless. For example, Scala has a BigDecimal
type with a +
method which means you can add two big decimals with much less code than in Java.
Compare the following Scala to Java:
// scala
val
total
=
BigDecimal
(
10000
)
+
BigDecimal
(
200
)
// java
BigDecimal
total
=
new
BigDecimal
(
10000
).
add
(
new
BigDecimal
(
200
));
Scala hasn’t reimplemented Java’s BigDecimal
; it just delegates to it and saves you having to type all that boilerplate.