Installation
- Section
Other ways to install Scala
, download for Linux (link) - Define
SCALA_HOME
based on link - Add
$SCALA_HOME/bin
to$PATH
Now, create file HelloWorld.scala
:
1
2
3
4
5
6
7
object HelloWorld {
def main(args: Array[String]): Unit = {
val language = "Scala"
val msg = s"Hello World, My Lang is ${language}"
println(msg)
}
}
To Compile & Run:
1
2
3
scalac HelloWorld.scala
scala -cp . HelloWorld
java -cp .:$SCALA_HOME/lib/* HelloWorld
- Line 2, use
scala
command to execute the class - Since
scalac
generates bytecode, line 3 executes class byjava
command with extra classpath
The HelloWorld
class can be rewritten as
1
2
3
4
object HelloWorld extends App {
val language = "Scala"
println(s"Hello World, My Lang is $language")
}
Basics
- No semicolon to terminate lines, unless multiple expressions on the same line
- No need for identical name of the class & its file
- in spite of Java, Scala file names do not need to be the same as class names
- No need of directory structure corresponding to package hierarchy
- however, the correspondence is recommended
- No need to
public
modifier- by default everything is public
- Same operator precedence as in Java
- In fact all operators are the object methods using as the infix operator syntax
Unit
of Scala =void
of Java- however
Unit
has a single value which is()
- however
Any
of Scala =Object
of Java- No need using
return
- automatically the value of last expression of every block is returned
- No checked exception
- No try-with-resource as in Java
- No
?:
operator in Scala, useif-else
instead (section) - No primitive type in Scala, everything is object (the reason Scala is pure OO while Java is not)
- Relaxed method name, such as
+
,*
, …, similar to operator overloading- In fact
2 + 3
is2.+(3)
- In fact
- No enum keyword and enumeration like Java
- use
object
extendsEnumeration
- use
- Using
object
keyword to create singleton objects in Scala- Adding methods to these objects is like static methods in Java, and they are accessible by object name
- So no
static
in Scala
Define a constant:
val
ID[:TYPE] = VALUE
Define a variable:
var
ID[:TYPE] = VALUE
1
2
3
4
5
val xmax, ymax = 100
var greeting, message: String = null
// assign two different constants using a tuple in a single line
val (sum, count) = (1.0, 1)
Note: In Scala, use val
unless you really need to change the contents! (Scala loves immutability)
Support Java classes:
1
2
3
4
5
6
7
8
9
10
import java.util.{Date, Locale}
import java.text.DateFormat._
object FrenchDate {
def main(args: Array[String]) {
val now = new Date
val df = getDateInstance(LONG, Locale.FRANCE)
println(df format now)
}
}
TODO: look lazy
val
Control Structures
- In Java these two have differences:
- expression, such as
3 + 4
, has a value - statement, such as
if(...) ...
, carries out an action
- expression, such as
- In Scala, almost all constructs have values
- An
if
expression has a value - A block has a value - the value of its last expression
- An
- Scala does not have
?:
operator! Useif else
instead
1
2
3
4
5
6
7
8
9
val x: Double = Math.random()
val b: Boolean = if (x > .5) true else false
val a: Any = if (x > .5) true else "Oops!"
val u: Any = if (x > .5) true // or if (x > .5) true else ()
val distance = {val dx = x-x0; val dy = y-y0; sqrt(dx * dx + dy * dy)}
val q: Double = if (x >= 0) Math.sqrt(x) else
throw new IllegalArgumentException("No Negative")
- Line 3, the if-else has a definite
Boolean
return type - Line 4, each branch has a different type, so the variable is of type
Any
(it is the common supertype), however the returning object isBoolean
orString
- Line 5, like line 4, but the returning object is
Boolean
orUnit
.Unit
type has one value which is()
- Line 6, the value of
sqrt()
is assigned todistance
(a good practice to initialize aval
) - Line 8, the type of
if
isDouble
and the type ofelse
isNothing
which is ignored!
Loops
while
is the same as Java
while (BOOLEAN_EXPRESSION) …
1
2
3
4
5
6
var (r, n) = (1.0, 10) // multiple variables by tuple, r:Double, n:Int
while (n > 0) {
r = r * n
n -= 1
}
println(s"r = $r")
Scala has no loop of for (initialize; test; update)
as Java. Instead, the syntax of for is
1
2
3
4
5
for (i <- 1 to 10) {
val x: Double = Math.random()
val u: Any = if (x > .5) true
println(f"i = $i%02d, u = $u")
}
1
2
3
4
val s = "Hello"
var sum = 0
for (i <- 0 until s.length) // or for (i <- 0 to s.length - 1)
sum += s(i)
or
1
2
var sum = 0
for (ch <- "Hello") sum += ch
Note: Scala’s Int
has a special API for range
- [a, b]:
1.to(5)
or1 to 5
- [a, b):
1.until(5)
or1 until 5
Advanced for
1
2
3
4
5
6
7
8
9
10
11
12
13
// multiple generators: variable <- expression
for (i <- 1 to 3; j <- 1 to 3) print(f"${10 * i + j}%3d") // 11 12 13 21 22 23 31 32 33
// second generator with a 'guard' starting with an 'if'
for (i <- 1 to 3; j <- 1 to 3 if i != j) print(f"${10 * i + j}%3d") // 12 13 21 23 31 32
// using one or more definitions, e.g. the 'from' var
for (i <- 1 to 3; from = 4 - i; j <- from to 3) print(f"${10 * i + j}%3d") // 13 22 23 31 32 33
//or
for {i <- 1 to 3
from = 4 - i
j <- from to 3}
print(f"${10 * i + j}%3d")
for Comprehension
A
for
loop withyield
returns a sequence
1
2
3
4
5
val v = for (i <- 1 to 10) yield i % 3 // v is Vector(1, 2, 0, 1, 2, 0, 1, 2, 0, 1)
v.foreach(println)
val str: String = for (c <- "Hello"; i <- 0 to 1) yield (c + i).toChar // HIeflmlmop
val vc = for (i <- 0 to 1; c <- "Hello") yield (c + i).toChar // Vector(H, e, l, l, o, I, f, m, m, p)
Note: Lines 4 & 5 look similar, but the results are different. So the generated collection is compatible with the first generator.
Function & Procedure
- Scala functions are like static methods in Java (C++ also has functions)
- No need to specify the return type, unless the function is recursive
1
2
3
4
5
6
7
8
9
10
def abs(x: Double) = if (x >= 0) x else -x
def fac(n : Int) = {
var r = 1
for (i <- 1 to n) r = r * i
r // no need to use return keyword
}
// recursive factorial must declare Int as return
def fac(n: Int): Int = if (n <= 0) 1 else n * fac(n - 1)
Possible to define default value for parameters! To set a specific parameter, use named arguments!
1
2
3
4
5
def decorate(str: String, left: String = "[", right: String = "]") = left + str + right
println(decorate("Hello")) // [Hello]
println(decorate("Hello", "(")) // (Hello]
println(decorate("Hello", right = ")")) // [Hello)
A procedure is just a named block with 0-to-many parameter(s), but without any return value. So in its syntax no =
.
1
2
3
4
def box(s : String) {
val border = "-" * (s.length + 2)
print(f"$border%n|$s|%n$border%n")
}
Scala has another way for varargs in Java
- The type of
args
isSeq
- Like Java, these type of parameters must be defined as last ones
- Note: Not possible to set default value for other parameters when such parameter is declared
1
2
3
4
5
6
7
8
9
10
def sum(args: Int*) = {
var result = 0
for (arg <- args) result += arg
result
}
// to call:
println(sum(1, 3, 66)) // ok
println(sum(1 to 5)) // error
println(sum(1 to 5: _*)) // consider 1 to 5 as an argument sequence
Arrays
Fixed-size Arrays
- Implemented as a Java array
1
2
3
4
5
6
val nums = new Array[Int](10) // All initialized with zero
val a = new Array[String](10) // All initialized with null
val s = Array("Hello", "World") // No new operator!
s(0) = "Goodbye" // Access element by (INDEX) not [INDEX]
Variable-Length Arrays: ArrayBuffer
1
2
3
4
5
6
7
8
9
10
11
import scala.collection.mutable.ArrayBuffer
val b = ArrayBuffer[Int]() // also new ArrayBuffer[Int]
b += 1 // append an element with +=
b += (1, 2, 3, 5) // append multiple elements
b ++= Array(8, 13, 21) // append any collection with ++=
b.trimEnd(5) // remove the last five elements
b.trimStart(1) // remove the first element
b.insert(2, 6, 7) // insert one or more elements from index 2
b.remove(2) // remove element at index
b.remove(2, 3) // from index 2, remove 3 elements
Traversing
1
2
3
4
5
6
7
8
9
10
val b = ArrayBuffer[Int](1, 6, 10)
for(i <- 0 until b.length) // all collections
println(s"$i = ${b(i)}")
for(i <- b.indices) // all indexed-collections (array, array buffer, list, ...)
println(s"$i = ${b(i)}")
for(e <- b)
println(s"element = $e")
It is possible to use for comprehension for array & array buffer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
val b = ArrayBuffer[Int](1, 6, 10)
val p1: ArrayBuffer[Int] = for(e <- b) yield e * e
val p2: Array[Int] = for(e <- b.toArray) yield e * e
val p3: ArrayBuffer[Int] = b.map(Math.pow(_, 2).toInt)
val result = for (e <- b if e >= 0) yield e
val positionsToRemove = for (i <- b.indices if b(i) < 0) yield i
for(i <- positionsToRemove.reverse) b.remove(i)
val positionsToKeep = for (i <- b.indices if b(i) >= 0) yield i
for (j <- positionsToKeep.indices) b(j) = b(positionsToKeep(j))
b.trimEnd(b.length - positionsToKeep.length)
p3
has the same result asp2
- Line 7, returns only positive elements but in a new ArrayBuffer
- Lines 9-10 try to remove negative numbers from the original array buffer
- Lines 12-13 try to remove negative numbers from the original array buffer, another way
Collections
Use List()
, Set()
, and Map()
to create an immutable instance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
val list = List(1, 2, 3)
list.foreach(value => println(value))
list.foreach(println)
// defining type of 'scores' is redundant!
val scores: Map[String, Int] = Map(("A", 1), ("b", 20), "C" -> 300)
scores.foreach((entry: (String, Int)) =>
println(f"K=${entry._1}, V=${entry._2}%03d")) // formatted string
//or
for ((k, v) <- scores)
println(f"K=$k, V=$v%03d")
val r = if(scores.contains("A")) scores("A") else 0
// or
val r = scores.getOrElse("A", 0)
val swap: Map[Int, String] = for ((k, v) <- scores) yield (v, k) // Switch key & value
- Line 7,
- The
entry
is a tuple of(String, Int)
- Note: Unlike array or string positions, the component positions of a tuple start with 1, not 0.
- The
f
makes the string formatted and acts likeprintf
in Java. The%03d
pads the${entry._2}
with max two leading zeros
- The
For mutable ones
1
2
3
4
5
// M A P
val updatingScores = scala.collection.mutable.SortedMap[String, Int]()
updatingScores("A") = 8
updatingScores += (("D", 10), "B" -> 10, "C" -> 7)
updatingScores -= "C" // Remove by key
Java and Scala Interoperability
- Scala arrays are implemented as Java arrays, so you can pass them back and forth between Java and Scala
- Java’s collection needs conversion if Scala’s type are required!
import scala.collection.JavaConverters._
forasScala
import scala.collection.mutable
the returning object is mutable
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import scala.collection.JavaConverters._
import scala.collection.Searching.search
import scala.collection.mutable
import java.util.Arrays.binarySearch
//...
val myJava = new MyJava
val fromJavaArr = myJava.getArrayOfString
println(fromJavaArr.mkString("{", ", ", ")")) // output: {Hello, World)
println(myJava.sumArrayOfInteger(Array(1, 2, 4))) // output: 7
// Error, no specific method for String type, although there is one for Object!
// java.util.Arrays.binarySearch(Array("b", "a").sorted, "a")
val arr = Array("b", "a", "d").sorted
println(s"idx=${binarySearch(arr.asInstanceOf[Array[Object]], "b")}") // output: idx=1
println(s"idx=${arr.search("c")}") // output: idx=InsertionPoint(2)
val fromJavaList = myJava.getListOfInteger
println(fromJavaList.stream().mapToInt(i => i).sum()) // like Java i -> i
println(fromJavaList.stream().mapToInt(_.intValue).sum) // like Java i -> i.intValue
val toScalaList: mutable.Seq[Int] = myJava.getListOfInteger
.asScala // 1. convert list of Java to Scala
.map(_.toInt) // 2. convert Java's Integer to Scala's Int
println(toScalaList.sum) // the sum needs 'List' & 'Int' of Scala
val toScalaMap: mutable.Map[String, Long] = myJava.getAMap
.asScala
.map((t: (String, lang.Long)) => (t._1, t._2.toLong))
println(toScalaMap) // Map(A -> 0, B -> 5, C -> 10)
- Line 18, call Java’s
binarySearch
and passing an array of String, however a type conversion is necessary - Line 19
search
method requiresimport scala.collection.Searching.search
for binary search- two types of result:
Found(n)
: the element is found at index nInsertionPoint(n)
: not found, however proposed insertion at index n (above, insert ‘c’ at index 2)
- Line 27-30: two steps of conversion from Java to Scala
- Line 28: call
asScala
(applicable on all Java collections) to convert to Scala equivalent - Line 29: call
map
to convert Java’sInteger
to Scala’sInt
- Line 28: call