On our tour we’ve seen some example syntax, walked through the class hierarchy, and briefly looked at ScalaDoc, but Scala offers heaps of other interesting language features.
In this chapter, we won’t really talk about syntax but we’ll discuss some of the things that make Scala an interesting and powerful language when working with source code, working with methods, and using its functional programming features.
Source Files. What you put in source files is much more flexible in Scala than in Java. There’s no restriction on what a .scala
file contains. A file called Customer.scala
might contain a class called Customer
, but it doesn’t have to. Similarly, it might contain four classes, none of which are called Customer
.
Packages. Packages are similar. Although they are essentially the same thing as in Java, classes in packages don’t have to live in folders of the same name like they do in Java. There are some differences in scoping; for example, there’s no protected
keyword in Scala but you can use special syntax (variable[package]
) to achieve the same thing.
Package Objects. Scala also has the idea of package objects. These are objects that you can put useful chunks of code in, for re-use within the package scope. They’re available to other classes in the package, and if someone imports that package, everything within the package object is available to them too. Libraries often use these to allow you to import all their classes in one go.
Import Alias. Imports are about the same as in Java but once you’ve imported a class in Scala, you can rename it within your class. In other words, you can create an alias for a class within your class. This can be useful when you’ve got a name clash, for example between libraries.
Type Aliases. Scala also supports type aliases. You can give an alias to a complex type definition to help clarify the intent. It’s similar to a structureless typedef
or #define
macro in C, or what’s called type synonyms in Haskell.
Traits. Although Scala has classes and objects, there is no “interface” keyword. Instead, there is the idea of a trait
which is similar to an interface but can have methods. It’s somewhere between Java 8’s default methods and Ruby’s mixins.
Generics. There’s better support for generic covariance and contravariance in Scala than Java. This means that you can be more general and more flexible in your method signatures when generic types are used as arguments.
class
Stack
[
+A
]
{
def
push
[
B
>:
A
](
b
:
B
)
:
Stack
[
B
]
=
...
}
Variable Arguments. When working with methods, Scala supports variable arguments or varargs
just like Java. They look different (def sum(numbers: Int*)
), but behave as you’d expect.
public
add
(
String
...
names
)
// java
def
add
(
names
:
String*
)
// scala
Named Method Arguments. Something Java doesn’t offer is named method arguments and default values. In Scala, you can call a method with its arguments out of order, as long as you name them. So, given the function def swap(first: Int, second: Int)
, you can call it explicitly, naming its arguments. Because they’re named, the compiler can work out which is which regardless of their position. So the following is fine:
swap
(
first
=
3
,
second
=
1
)
swap
(
second
=
1
,
first
=
3
)
Default Values. You can add a default value by using =
after the parameter declaration. For example, def swap(first: Int, second: Int = 1)
. The second value will default to 1
if you leave it off when you call the function. You can still supply a value to override the default, and still use named parameters.
swap
(
3
)
swap
(
3
,
2
)
swap
(
first
=
3
)
swap
(
first
=
3
,
second
=
1
)
Lambdas. Scala supports lambdas or anonymous functions. You can pass function literals as arguments to methods and use a function signature as an argument in a method signature. So the test
method below takes a function with no arguments which returns a Boolean
.
def
test
(
f
:
()
=>
Boolean
)
=
...
When you call it, you can pass in a function literal as a parameter.
test
(()
=>
if
(!
tuesday
)
true
else
false
)
As another example, you can create a function signature to represent a function from a String
value to a Boolean
like this:
def
test
(
f
:
String
=>
Boolean
)
:
Boolean
=
...
and call it with a function literal like this:
test
(
value
=>
if
(
value
==
"tuesday"
)
true
else
false
)
Some other Scala features aimed more at functional programming include:
Pattern matching. This is a hugely powerful feature which at first blush looks similar to switches but can be used for much more.
For comprehensions. For comprehensions are subtly different than regular for loops, and are useful when working with functional constructs. When you first encounter them, they’ll look like an alternative syntax to Java’s for
loop.
Currying. Although you can write your own currying functions in any language, Scala supports currying as a language feature. If you’re unsure what currying is, you probably don’t need to worry about it right now. See the for more details.
Functional Literals. The language supports literals to represent some useful types like tuples. Popular Java functional libraries like or have these kinds of things; Scala just makes them easier to work with.
Recursion. Most languages support recursion, but Scala has compiler support for tail call optimisation, which means it can support recursive calls that would result in a stack overflow in languages like Java. The compiler can even perform some checks for you if you use the @tailrec
annotation.