Scala Syntax for Java Developers
By: Dave Taubler
Tags: Software Development, Scala, Java, Software Engineering, Programming

Scala Syntax for Java Developers

A quick guide to some of the common syntactical differences between Scala and Java

Photo by Anastasia Zhenina on Unsplash

Java developers who are curious about Scala might find the differences in syntax to be daunting. So below is a guide to some of the common Scala syntax rules that differ from Java’s. At the end, we’ll walk through a comprehensive example of Scala code.

Note that we won’t explore the advanced, powerful features of Scala here. For-comprehensions, implicits, pattern-matching, and other features are better discussed in dedicated articles. Instead, this article will help any Java developers who are interested in exploring Scala, but are simply having a hard time understanding the code examples they’re seeing.

Let’s dive in.

Type declarations come after variable names

In Java, we declare types, then variable names, like so:

Animal a = getAnimal();

In Scala, the type comes afterwards:

val a: Animal = getAnimal();

Variables are declared as either immutable (val) or mutable (var)

Rather than beginning a variable declaration with the class name, as we do in Java, we begin them with either val or var in Scala.

val (short for value) indicates that the variable is immutable (similar to final in Java):

val a: Animal = getAnimal(); // `a` cannot be subsequently reassigned

var (short for variable) indicates that the variable is mutable:

var a: Animal = getAnimal(); // `a` can be subsequently reassigned

Scala favors immutability, so most often, you’ll see val used.

Type declarations can usually be omitted

Scala has very strong type inference. So we rarely need to explicitly declare the type. That’s one reason why the class comes last when declaring a variable; it’s easy to simply omit it that way:

var a = getAnimal(); // `a` is still an instance of Animal

Semicolons are not required to denote the end of a line

Semicolons are really only needed when multiple statements occur in the same line:

var a = getAnimal(); println(a)

We can, and almost always do, omit semicolons at the end of lines:

var a = getAnimal()

Methods and functions are declared with the ‘def’ keyword

Methods and functions (a function is like a method, but doesn’t belong to any class) are declared with the def keyword. Their return types are declared after the function name and a colon. And their bodies typically come after an equals sign:

def getAnimal(): Animal = {

// body goes here ...

}

When calling a no-argument method or function, the parenthesis can be omitted

In Java, we always provide the parentheses when invoking a method:

Animal a = getAnimal();

When calling a Scala function or method that takes no arguments, the parenthesis can be omitted:

val a = getAnimal()

// or

val a = getAnimal

Methods and functions do not need to explicitly return anything

The last value or expression in a method or function becomes the implicit return value:

def add(x: Int, y: Int): Int = {

x + y // the sum of x and y will be implicitly be returned

}

So while the return keyword exists in Scala, it is almost never used.

Unit means void

If a Java method has no return value, then it is declared as returning void:

public void output(String s) {

System.out.println(s);

}

In Scala, the return type would be declared as Unit:

def output(s: String): Unit = {

println(s)

}

Methods and functions rarely need to declare their return type

Scala’s strong type inference means that we rarely need to declare the return type of our functions/methods:

def add(x: Int, y: Int) = {

x + y // The compiler infers that an Int will be returned

}

Note that we still need to declare the return type in cases where:

the method is abstract

we want to return a superclass of the inferred return type

we simply want to be more clear

def getAnimal(): Animal {

// if we omitted the return type, Scala would infer Dog

new Dog()

}

Methods and functions do not require braces

If the body of a method or function does not span more than one line (or expression), then the braces can be omitted:

def add(x: Int, y: Int) =

x + y // or def add(x: Int, y: Int) = x + y

Methods and functions can be invoked with named arguments

In Java, when invoking a method that accepts multiple parameters, we cannot include the parameter names. Instead, we simply pass the values, in the order in which they are declared in the method:

int i = add(6, 8);

In Scala, we can choose to either call a method/function the “Java way”, or we can include all, or some, of the parameter names:

val i = add(6, 8)

// or

val i = add(x = 6, y = 8)

// or

val i = add(x = 6, 8)

// note: below will not compile:

val i = add(6, y = 8)

Traits instead of interfaces

While Java offers interfaces, Scala offers traits. Like Java interfaces, traits can include abstract methods. And like Java interfaces’ default methods, traits can offer implemented methods. Unlike interfaces, traits can also include variables.

trait Shape {

val point: Point

def print() // no implementation, so implicitly abstract

}

`extends` and `with`

As in Java, Scala classes cannot extend multiple parent classes. Also similar to Java, Scala classes can implement (or in Scala parlance, extend) multiple traits.

When a Scala class extends either a class or a trait, the extends keyword is used. If the class then extends any additional traits, then the with keyword is used:

class Shape { ... } trait Drawable { ... } trait Movable { ... } class Circle extends Shape with Drawable with Movable class Raster extends Drawable with Movable

Classes are declared with their members immediately listed in parenthesis

Whereas we might define a simple class in Java like this:

public class Foo {

private String a;

private int b;

// getters, setters, constructors, etc

}

We would typically define a Scala class all in one line, like so:

class Foo(a: String, b: Int) { }

Brackets are optional, unless additional members, methods, etc, need to be declared:

class Foo(a: String, b: Int)

Case classes represent specialized data classes

Java 14 introduced the concept of records. If you’re familiar with records, then you’ll quickly become familiar with case classes. Otherwise, case classes are special data classes. Their syntax is similar to regular classes:

case class Foo(a: String, b: Int)

However, the compiler auto-generated the standard methods required of a data class, such as equals() , hashCode() and toString() methods, getters, etc.

It also generates a “companion object” with an apply() and unapply() method (we’ll discuss these a bit later).

Generic types are denoted by square brackets ( [] ), rather than angle brackets ( <> )

In Java, we denote a generic type using angle brackets:

List<String> list;

In Scala, square brackets are used to denote generic types:

List[String] list

String interpolation is done with String literals prefixed by `s`

In Java, to quickly concatenate Strings, we use the plus (+) symbol:

System.out.println("Hello, "+ firstName + " "+ lastName + "!");

In Scala, we perform String interpolation by prefixing a String literal with s, and by referring to variables with the dollar sign ($):

println(s"Hello, $firstName $lastName!")

To access a nested value, or an expression, curly braces are used:

println(s"Hello, ${name.first} ${name.middle.getOrElse("")} ${name.last}!")

Anonymous functions look similar to Java lambdas, but with the => symbol

Java provides syntactic sugar when constructing lambda functions:

list.foreach(item -> System.out.println("* " + item));

In Scala, we use the => symbol with anonymous functions:

list.foreach(item => println(s"* $item"))

Underscores serve as a placeholder

Underscores are used by Scala as placeholders. You’ll see underscores in a few different use cases, including:

Anonymous functions:

Rather than declaring a variable in the anonymous function:

list.foreach(item => println(item))

We can omit the item variable and replace it with an underscore:

list.foreach(println(_))

Note, similar to Java, we can simplify the line above by providing a reference to the println function (which will implicitly take the current variable as an argument):

list.foreach(println)

Pattern matching:

When we perform pattern matching, we are attempting to match an instance with its type. Say we have a case class like so:

case class Dog(name: String) extends Animal

If we have an instance a of an Animal, we may try to determine if it is a Dog with a particular name:

a match {

case Dog(name) => println(s"Found a dog named $name");

...

}

Or, we might not care about the Dog’s name. Dog ’s constructor still takes an argument, so we can simply use the underscore as a placeholder:

a match {

case Dog(_) => println(s"Found some dog");

...

}

Likewise, we might want to know if we’ve found a Dog , or anything other than a Dog . Again we’d use an underscore:

a match {

case Dog(_) => println(s"Found a dog");

case _ => println("Found something other than a dog")

}

Companion objects instead of statics

Java has the static keyword, which describes variables and methods that are tied to a particular class, rather than instances of an object.

public class UserManager {

public static void save(User u) {

//...

}

} // we can invoke that static method via: UserManager.save(new User("Pat"));

Scala does not offer the static keyword. However, with Scala, we can create objects, which effectively offer the same thing:

object User {

def save(u: User) = {

// ...

}

} // we can invoke that object's method via: User.save(new User("Pat"))

Moreover, Scala allows us to create “companion objects”; that is, objects that correspond with identically-named classes:

class User(name: String) {

} // This is the companion object to the User class

object User {

def apply() {

new User("Noname")

}

def apply(name: String) = {

new User(name)

}

def unapply(u: User) = {

Some(u.name)

}

def save(u: User) = {

// ...

}

}

While we can define any functions in a class’ companion object, the apply() and unapply() methods have special meaning.

If we were to call the object’s name, followed by parenthesis with any number of arguments, the corresponding apply(…) method in the object would be invoked.

In the above example, invoking User() would, under the covers, cause User.apply() to be invoked; invoking User("Chris") would actually invoke User.apply("Chris") . Commonly, an instance of the companion class is constructed and returned. In this manner, a companion object’s apply() method(s) can — and typically are — used as factory methods.

For this reason, we will typically see objects being created like this:

val u = User("Suzie")

instead of this:

val u = new User("Suzie")

On the other hand, unapply() is used as a deconstructor. Whenever the individual components of a class are needed (such as during pattern matching), its unapply() method is invoked behind the scenes.

A comprehensive example

Let’s analyze a block of Scala code based on what we’ve just gone over:

case class Point(x: Int, y: Int) // 1



trait Drawable { // 2

def draw(): Unit // 3

}



abstract class Shape(val center: Point) { // 4

def area(): Double

}



case class Circle(override val center: Point, radius: Int) extends Shape(center) with Drawable { // 5

override def draw(): Unit = { // 6

println(s"⊙ ")

}

override def area() = Math.PI * (radius * radius)

def growBy(add: Int) = Circle(center, radius + add) // 7

}



case class Square(override val center: Point, side: Int) extends Shape(center) with Drawable {

override def draw(): Unit = {

println("◼︎")

}

override def area() = Math.PI * (side * side)

}



object Canvas { // 8



def main(args: Array[String]): Unit = { // 9

val circle1 = Circle(Point(50, 25), radius = 10) // 10

val circle2 = circle1.growBy(5)

val square = Square(Point(100, 150), side = 30)



val l: List[Drawable] = List(circle1, circle2, square) // 11

l.foreach(_.draw()) // 12



l.map(d => d match { // 13

case Circle(_, r) => s"Circle of radius $r" // 14

case Square(_, s) => s"Square of side length $s"

case _ => "Unknown shape" // 15

}).foreach(println) // 16

}



}