This is a memo for reading the book “Scala for the Impatient”
Chapter 1 The Basics
1.3 Commonly Used Types
Like Java, Scala has 7 numeric types: Byte, Char, Short, Int, Long, Float, and Double. Unlike Java, these types are classes, you can invoke methods on numbers.
1 | "Hello".intersect("World") // Yields "lo" |
In this expression, the java.lang.String
object “Hello” is implicitly converted to a StringOps
object, and then the intersect
method of the StringOps
class is applied. So remember to look into the StringOps class when you use the Scala docs.
Similarly, there are RichInt
, RichDouble
…
Arithmetic and Operator Overloading
1 | counter += 1 // Increments counter -- Scala has no ++ |
Input and Output
1 | println("Answer: " + 42) |
Chapter 2 Control Structure and Functions
2.5 Loops
until keyword
1 | val s = "Hello" |
break out of loops
Scala has no break
or continue
statements to break out of a loop. Here are a few options for break
:
- Use a Boolean control variable
- Use nested functions -
return
from the middle of a function - Use the
break
method in theBreaks
object:import scala.until.control.Breaks._
Try to avoid 3) if possible
advanced for loops
1 | for (i <- 1 to 3; j <- 1 to 3 if i != j) print((10*i*j)+" ") |
advanced for comprehensions
The generated collection is compatible with the first generator
1 | for (c <- "hoho"; i <- 0 to 1) yield (c + i).toChar |
2.7 Functions
As long as the function is not recursive, you don’t have to specify the return type.
Variable Arguments
1 | def sum(args: Int*) = { |
Now, you can pass as many arguments as you want: sum(1,2,3,4)
. However, you can’t pass a sequence to it. The remedy is to tell the compiler that you want the parameter to be considered as an argument sequence. Append : _ like this `sum(1 to 5: _)`
1 | sum(1, 2, 3, 4) // 10 |
This call syntax is needed in a recursive definition
1 | def recursiveSum(args: Int*): Int = { |
2.10 Procedures
If a function body is enclosed in braces w/o a proceeding = symbol, then the return type is Unit
. Such a function is called a procedure.
2.11 Exceptions
Scala exceptions work the same way as in Java. When you throw an exception,
1 | throw new IllegalArgumentException("x should be positive") |
the current computation is aborted, and the runtime system looks for an exception handler that can accept an IllegalArgumentException
. Control resumes with the innermost such handler. If no such handler exists, the program terminates.
A throw
expr has the special type Nothing
. This is useful in if/else
expr. If one branch has type Nothing
, the type of the if/else
expr is the type of the other branch.
The syntax for catching exceptions:
1 | try { |
Note that the more general exception types should come after the more specific ones.
Chapter 3 Working with Arrays
3.2 Variable-length Arrays: Array Buffers
1 | import scala.collection.mutable.ArrayBuffer |
Adding or removing elements at the end of an array buffer is an efficient operation
3.4 Transforming Arrays
1 | a.filter(_ % 2).map(2 * _) |
3.5 Common Algorithms
sorted
sorts an array or array buffer and returns a new, w/o modifying the original
1 | val b = ArrayBuffer(1, 7, 2, 9) |
You can sort an array, but not an array buffer, in place:
1 | val a = Array(1, 7, 2, 9) |
mkString
1 | a.mkString("<", ",", ">") |
3.6 Deciphering Scaladoc
def appendAll(xs: TraversableOnce[A]): Unit
The xs
parameter can be any collection with the TraversableOnce
trait, the most general trait in the scala collection hierarchy. Just think of any collection when you see TraversableOnce
, Traversable
, and Iterable
When you see Seq
, this trait requires element access by an integer index, so think of array, list or string
3.7 Multidimensional Arrays
Multidimensional arrays are implemented as arrays of arrays
1 | val mx = Array.ofDim[Double](3, 4) |
Exercise
Q2. write a loop that swaps adjacent elements of an array of integers. Array(1,2,3,4,5) => Array(2,1,4,3,5)
1 | def swapEveryTwo(a: Array[Int]) = { |
Q3. Repeat 2), but produce a new array with the swapped values. Use for/yield
1 | for (i <- 0 until (a.length, 2); j <- (i+1) to (i, -1) if (j < a.length)) yield a(j) |
Chapter 4 Maps and Tuples
4.1 Constructing a Map
The default Map is immutable. To use a mutable map,
1 | import scala.collection.mutable.Map as mutMap |
In a mutable map, you can update a map value, or add a new one.
1 | scores("Bob") = 5 |
4.2 Accessing Map Values
1 | val bobsScore = scores.getOrElse("Bob", 0) |
If you use scores.get
the return type will be an Option
object, that ie either Some(value for key)
or None
.
4.4 Iterating over Maps
1 | for ((k, v) <- map) process k and v |
Chapter 5 Classes
- Fields in classes automatically come with getters and setters
- You can replace a field with a custom getter/setter w/o changing the client of a class
- Use the
@BeanProperty
annotation to generate the JavaBeans getXxx/set Xxx methods - It is considered good style to use
()
for a mutator method, and to drop () for an accessor method
Properties with Getters and Setters
1 | class Person { |
Scala generates a class for the JVM with a private age
field and getter and setter methods. These methods are public because we did not declare age
as private
At any time, you can redefine the getter and setter methods yourself. For example,
1 | class Person { |
If a field is
val
, only a getter method will be initiated.
5.4 Object-private Fields
In scala, a method can access the private fields of all objects of its class. For example
1 | class Counter { |
If you don’t want this, scala allows an even more severe access restriction, with private[this] var value = 0
. Now, the methods of the Counter
class can only access the value
field of the current object, not others. And no getter and setter is generated for that field.
Scala allows you to grant access rights to specific classes.
private[ClassName]
will give specific class access to the field.
5.5 Constrcutors
1 | // Here is a class with auxiliary constructors |
Each auxiliary constructors must start with a call to previously defined auxiliary constructor or the primary constructor
Primary Constructor
- The parameters of the primary constructor are placed immediately after the class name
- The primary constructor executes all statements in the class definition
1 | class Person(val name: String, val age: Int) { |
the println
is part of the primary constructor.
You can often eliminate auxiliary constructors by using default arguments in the primary constructor:
class Person(val name: String = "", val age: Int = 0)
To make the primary constructor private, place the keyword private
like this: class Person private(val id: Int){...}
. Then a class user must use an auxiliary constructor to construct a Person
object.
5.8 Nested Classes
In scala, you can nest just about anything inside anything.
1 | import scala.collection.mutable.ArrayBuffer |
chatter.Member
and worker.Member
are different classes. Because in scala, each instance has its own class Member
. In our network example, you can add a member within its own network, but not across networks.
1 | val fred = chatter.join("Fred") |
If you don’t want such behaviour, a good workaround is to place the Member
definition to the Network
companion object.
Chapter 6 Objects
6.1 Singletons
Scala has no static methods or fields. Instead, you use the object
constructor. The constructor of an object is executed when the object is first used. If an object is never used, its constructor is not executed.
An object can have essentially all the features of a class - it can even extend other classes or traits. Just one exception: You cannot provide constructor parameters.
- As a home for utility functions or constants
- when a single immutable instance can be shared efficiently
- when a single instance is required to coordinate some service
6.2 Companion Object
1 | class Account { |
The class and its companion object can access each other’s private features. They must be located in the same source file
Note: the companion object is accessible, but it is not in scope. Need to call
Account.newUniqueNumber()
and notnewUniqueNumber()
directly.
6.4 The apply
method
It is common to have objects with an apply
method. Typically, such an apply
method returns an object of the companion class. But why doesn’t one just use a constructor? Well, not having the new
keyword is handy for nested expressions, such as Array(Array(1, 7), Array(2))
Caution: It is easy to confuse
Array(100)
andnew Array(100)
. The 1st yields an Array[Int] but the 2nd returns an Array[Nothing] with 100 null elements
6.5 Application Objects
Each scala program must start with an object’s main method of type Array[String] => Unit
. Instead of providing a main method for your application, you can extend App
trait and place the program code into the constructor body.
Packages and Imports
7.2 Scope Rules
You can access names from the enclosing scope. For example:
1 | package com { |
Everything in the parent package is in scope.
7.3 Chained Package Clauses
A package clause can contain a “chain”
1 | package com.horstmann.impatient { |
7.4 Top-of-File Notation
1 | package com.horstmann.impatient |
This is equivalent to 7.3
Note: everything in the file belongs to
com.horstmann.impatient.people
, but the packagecom.hostmann.impatient
has also been opened up so you can refer to its contents.
7.5 Package Objects
A package can contain classes, objects, and traits, but not the definitions of functions or variables. That’s an unfortunate limitation of the Java virtual machine. Now, package objects address this limitation.
Every package can have one package object. You define it in the parent package, and it has the same name as the child package. For example
1 | package com.horstmann.impatient |
Usually, the package objects is out in com/hostmann/impatient/people/package.scala
7.9 Renaming and Hiding Members
1 | import java.util.{HashMap => JavaHashMap} |
Chapter 8 Inheritance
8.2 Override and Invoke Super Method
1 | class Employee extends Person { |
8.3 Type Checks and Casts
To test whether an object belongs to a given class or its subclass, use isInstanceOf
method. If the test succeeds, you can use the asInstanceOf
to convert a reference to a subclass reference.
If you want to test whether p refers to a certain object (not its subclass), use if (p.getClass == classOf[Employee])
. However, we always use pattern matching for such use cases.