Book Image

Scala for Java Developers

By : Thomas Alexandre
Book Image

Scala for Java Developers

By: Thomas Alexandre

Overview of this book

Table of Contents (19 chapters)
Scala for Java Developers
Credits
Foreword
About the Author
Acknowledgments
About the Reviewers
www.PacktPub.com
Preface
Index

Learning Scala through the REPL


As a Java developer, an REPL may be new to you since there is no such thing for the Java language. It used to refer to the Lisp language interactive environment, and today, equivalent tools are available for many programming languages such as JavaScript, Clojure, Ruby, and Scala. It consists of a command-line shell where you can enter one or more expressions rather than complete files and get immediate feedback by evaluating the result. The REPL is a fantastic tool that helps us to learn all the Scala syntax because it compiles and executes every statement that you write using the full power of the compiler. In such an interactive environment, you get instant feedback on every line of code you write.

If you are new to Scala, we recommend that you carefully follow this REPL session as it will give you a lot of useful knowledge for programming with Scala.

Let's dive into some of the most apparent differences between Java and Scala in order to get acquainted with the Scala syntax.

Declaring val/var variables

In Java, you would declare a new variable by putting in order its type, followed by the name, and then the optional value:

String yourPast = "Good Java Programmer";

In Scala, the order of declaring the variable name and type is inverted, with the name appearing before its type. Let's enter the following line into the REPL:

scala> val yourPast : String = "Good Java Programmer"  [Hit Enter]
yourPast : String = "Good Java Programmer"

Inverting the order of declaring the variables, type, and name as compared to Java might at first seem a strange idea if you want to make it as easy as possible for a Java developer to grab the Scala syntax. However, it makes sense for several reasons:

  • The Scala compiler, in this case, is able to deduct the type automatically. You could (and probably should, for conciseness) omit this type by entering the equivalent but shorter line of code instead:

    scala> val yourPast = "Good Java Programmer"
    yourPast : String = "Good Java Programmer"
    

    This is the most basic illustration of what is called Type Inference, and you will see that the Scala compiler will try to deduct types whenever it can. If we had omitted this optional type but followed the Java syntax, the parsing done by the compiler would have been more difficult to implement.

  • In our opinion, it is more important to know a variable name than its type in order to understand the flow of a program (and therefore make it appear first); for instance, if you deal with a variable representing a social security number (ssn), we think the term ssn is more valuable than knowing if it is represented as a string or an integer or any other type.

You probably noticed the val variable in front of the declaration; it means that we explicitly declare the variable as immutable. We can try to modify it as shown in the following code snippet:

scala> yourPast = "Great Scala Programmer"
<console>:8: error: reassignment to val
  yourPast = "Great Scala Programmer"
           ^

The preceding code will not only give you a clear explanation of what was wrong but also the exact place where the parser did not agree (notice the ^ character precisely showing where the error lies in the line).

If we want to create a mutable variable instead, we should declare it with var as shown in the following code snippet:

scala> var yourFuture = "Good Java Programmer"
yourFuture: String = "Good Java Programmer"
scala> yourFuture = "Great Scala Programmer"
yourFuture: String = "Great Scala Programmer"

In summary, you cannot change yourPast but you can change yourFuture!

The semicolon at the end of the lines is optional in Scala; a small but pleasant feature of the language.

Let's move on to an important difference. In Java, you have primitive types such as int, char, or boolean (eight in total), as well as operators to manipulate data such as + or >. In Scala, there are only classes and objects, making Scala more "object-oriented" than Java in some way. For instance, enter the following value into the REPL:

scala> 3
res1: Int = 3

By default, the compiler created an immutable Int (integer) variable with the res1 name (that is, result 1) in case you need to reuse it later on.

Now, enter the following line in REPL:

scala> 3 + 2
res2: Int = 5

The preceding code resembles the usage of an operator (as in Java) but is in fact the invocation of a method named + called on object 3 with the input parameter 2, equivalent to the slightly less clear statement:

scala> (3).+(2)
res3: Int = 5

Syntactic sugar (that is, syntax designed to make things easier to read or express) was added here by removing the necessity to specify the parenthesis. This also means that we can now implement similar methods on our own defined types to express code elegantly. For example, we can express the addition of two Money objects of different currencies (note that the Money type does not exist in the default Scala library) by simply stating Money(10,"EUR") + Money(15,"USD"). Let's try to do that in the REPL.

Defining classes

First, we can define a new class Money that has a constructor parameter named amount of type Int as follows:

scala> class Money(amount:Int)
defined class Money

Note

Scala has a special syntax for declaring constructor parameters that will be explored in more depth later.

Now, we can create a Money instance as shown in the following code snippet:

scala> val notMuch = new Money(2)
notMuch : Money = Money@76eb235

You get back an object with its displayed reference. The REPL provides you with TAB completion, so type notMuch. and hit the Tab key:

scala> notMuch. [Tab]
asInstanceOf isInstanceOf toString

By using the preceding autocompletion, you will get suggestions of the available methods for that class, as you will get with most Java IDEs.

As shown previously, you can construct new instances of Money by invoking the constructor, but you do not have access to the amount variable since it is not a field. To make it a field of the Money class, you have to add a 'val' or 'var' declaration in front of it, as shown in the following code snippet:

scala> class Money(val amount:Int)
defined class Money

This time, instead of again typing the line that created an instance, we will use the up arrow (the shortcut to display previous expressions: the history of the console) and navigate to it:

scala> val notMuch = new Money(2)
notMuch : Money = Money@73cd15da

Note

The Tab key can be pressed at any time in the REPL and provides autocompletion.

Invoking autocompletion on this new instance will display the following:

scala> notMuch. [Tab ]
amount asInstanceOf isInstanceOf toString

So, we can simply read the value of the getter for this amount field by referring to it:

scala> notMuch.amount
res4: Int = 2

Similarly, if we had declared the amount to be a var variable instead of val, we would also have access to the setter method:

scala> class Money(var amount:Int)
defined class Money
scala> val notMuch = new Money(2)
notMuch: Money = Money@6517ff0
scala> notMuch. [ Tab ]
amount   amount_=   asInstanceOf   isInstanceOf   toString

The setter method is invoked when we use the following code snippet:

scala> notMuch.amount=3
notMuch.amount: Int = 3

Explaining case classes

As Java developers, we are accustomed to the JavaBean style domain classes that not only include fields with getters and setters but also constructors as well as hashCode, equals, and toString methods, as shown in the following code snippet:

public class Money {

    private Integer amount;
    private String currency;

    public Money(Integer amount, String currency) {

        this.amount = amount;
        this.currency = currency;

    }

    public Integer getAmount() {
        return amount;
    }

    public void setAmount(Integer amount) {
        this.amount = amount;
    }

    public String getCurrency() {
        return currency;
    }
    public void setCurrency(String currency) {
        this.currency = currency;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 29 * hash + (this.amount != null ? this.amount.hashCode() : 0);
        hash = 29 * hash + (this.currency != null ? this.currency.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {

        if (obj == null) {
            return false;
        }

        if (getClass() != obj.getClass()) {
            return false;
        }

        final Money other = (Money) obj;
        return true;
    }

    @Override
    public String toString() {
        return "Money{" + "amount=" + amount + ", currency=" + currency + '}';

    }
}

Achieving this in Scala is very straightforward and only requires the addition of the case word in front of the class declaration:

scala> case class Money(amount:Int=1, currency:String="USD")
defined class Money

We have just defined a class Money with two immutable fields named amount and currency with default values.

Without going too much into the details of the case classes, we can say that in addition to the preceding features of a traditional JavaBean style domain class, they have a powerful mechanism for pattern matching. The case word is analogous to the switch statement in Java, though it is more flexible, as we will see later on. The case classes contain additional features among which one is a factory method to create instances (no need to use the new keyword to create one).

By default, the fields declared in Scala classes are public, unlike Java, where they have a package-private scope, defined between private and protected. We could have written case class Money(private val amount: Int, private val currency: String) to make them private instead, or used var instead of val to make the fields mutable.

The shortest way to create an instance of Money is very straightforward:

scala> val defaultAmount = Money()
defaultAmount: Money = Money(1,USD)
scala> val fifteenDollars = Money(15,"USD")
fifteenDollars: Money = Money(15,USD)
scala> val fifteenDollars = Money(15)
fifteenDollars: Money = Money(15,USD) 

In the previous instance declaration, since only one parameter is given instead of two, the compiler matched it against the first declared field, that is, amount. Since the value 15 is of the same type as amount (that is, Integer), the compiler was able to populate the instance with this amount, using the default value "USD" as the currency.

Unlike the amount variable, invoking the Money constructor with the sole currency parameter will fail, as seen in the following statement:

scala> val someEuros = Money("EUR")
<console>:9: error: type mismatch;
 found   : String("EUR")
 required: Int
       val someEuros = Money("EUR")
                             ^

The preceding code does not work because the compiler could not guess which parameter we were referring to, and therefore tried to match them in order of declaration. To be able to use the default value for amount with the given "EUR" string, we need to include the parameter name explicitly, as shown in the following code snippet:

scala> val someEuros = Money(currency="EUR")
someEuros: Money = Money(1,EUR)

We could therefore also mark all parameters explicitly, which can be recommended when there are many parameters as shown in the following code snippet:

scala> val twentyEuros = Money(amount=20,currency="EUR")
twentyEuros: Money = Money(20,EUR)

An additional useful method when constructing instances is the copy method, which creates a new instance out of the original and eventually replaces given parameters:

scala> val tenEuros = twentyEuros.copy(10)
tenEuros: Money = Money(10,EUR)

We can use the copy method with explicitly named parameters, as follows:

scala> val twentyDollars = twentyEuros.copy(currency="USD")
twentyDollars: Money = Money(20,USD)

The copy method can be very useful when writing test fixtures, in particular, when the mockup instances to be initialized have constructors with many fields that are similar.

Let's move on by creating an addition operation of our Money class. For simplicity, we will pretend for a moment that we only deal with amounts of the same currency, the default USD.

In Java, we would probably add such a method with the following signature and simple content:

public class Money {

    Integer amount;
    String currency;

    public Money(Integer amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public Money add(Money other) {
        return new Money(this.amount +
        other.amount, this.currency);
    }
    ...
}

In Scala, we use the def keyword to define a class method or a function. In the REPL, we can have multiline expressions. The following case class declaration, containing the implementation of a summing method + is an example of such features:

scala> case class Money(val amount:Int=1, val currency:String="USD"){
     |   def +(other: Money) : Money = Money(amount + other.amount)
     | }
defined class Money

Notice that we can use + as a method name. We have also included the return type Money in the signature declaration, which is only optional since the type inference of Scala will deduct it, but including it explicitly is a good documentation practice for public methods (and methods are public by default if no other scope is specified). Moreover, in Scala, since the return word at the end of the method is optional, the last statement is always the one that is returned to the caller of the method. Furthermore, it is generally considered a good practice to omit the return keyword since it is not mandatory.

We can now write the addition of two Money instances with the following simple expression:

scala> Money(12) + Money(34)
res5: Money = Money(46,USD)

Things start becoming exciting once we start manipulating collections of objects, and the functional programming part of Scala helps very much for that matter. Since generics are part of the language (Java 5 onwards), Java can, for example, iterate over a list of integers by writing the following code snippet:

List<Integer> numbers = new ArrayList<Integer>();
numbers.add(1);
numbers.add(2);
numbers.add(5);
for(Integer n: numbers) {
    System.out.println("Number "+n);
}

The preceding code produces the following output:

Number 1
Number 2
Number 5

In Scala, the declaration of a list can be written as follows:

scala> val numbers = List(1,2,5)
numbers: List[Int] = List(1,2,5)

Scala collections systematically distinguish between immutable and mutable collections, but encourage immutability by constructing immutable collections by default. They simulate additions, updates, or removals by returning new collections from such operations instead of modifying them.

One way to print out the numbers is that we can follow Java's imperative style of programming and iterate over the collection by creating a for loop:

scala> for (n <- numbers) println("Number "+n)
Number 1
Number 2
Number 5

Another way to write the code in Scala (as well as many other languages on the JVM, such as Groovy, JRuby, or Jython) involves a more functional style, using lambda expressions (sometimes referred to as closures). In brief, lambdas are just functions that you can pass around as parameters. These functions take input parameters (in our case, the n integer) and return the last statement/line of their body. They are in the following form:

functionName { input =>
                body
             }

A typical example of lambda to iterate over the elements of the numbers list we have defined earlier, is given as follows:

scala> numbers.foreach { n:Int =>     
   | println("Number "+n)
   | }
Number 1
Number 2
Number 5

In that case, the body consists of only one statement (println...), and therefore returns Unit, that is, an empty result roughly equivalent to void in Java, except that void does not return anything.

As the time of writing this book, lambda expressions in Java are around the corner and will be introduced very soon as part of the JDK8 release, adopting a Scala-like style. Some of the functional constructs will therefore soon be available to Java developers.

It should become possible to write our tiny example in the following way:

numbers.forEach(n -> { System.out.println("Numbers "+n);});

As we stated previously, Scala collections are, by default, immutable. This is a very important aspect for making them behave as expected when dealing with multiprocessor architectures. One unique feature of the Scala collections compared to Java is that they include support for running operations in parallel.