Книга: Learn Scala for Java Developers
Назад: Installing Scala
Дальше: Scala’s Class Hierarchy

Some Basic Syntax

Defining Values and Variables

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 

Defining Functions

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.

Operator Overloading and Infix Notation

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.

Collections

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)   } 

Java Interoperability

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!" 

Primitive Types

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.

Назад: Installing Scala
Дальше: Scala’s Class Hierarchy