Scala Language Specification 2.12
Scala Fiddle An online playground for creating, sharing and embedding Scala fiddles (little Scala programs that run directly in your browser).
Examples drawn from https://www.tutorialspoint.com/scala/
- The name Scala stands for "Scalable Language".
- Everything is an object. Scala is object oriented.
- Every function returns a value and every value is an object, therefore every function is an object. Scala is Functional.
- Statically typed (Int, Boolean, Float, etc.)
- Runs on the JVM
- Interoperates seamlessly with all Java libraries.
- Can execute Java code
- Can do concurrent and synchronized processing
- There are no static methods in Scala. Note: static methods makes code confusing and hard to test.
- there are no such thing as primitives in Scala as there are in Java, thus everything is an object
println(1.toString) // String = 1
- -> is used for maps: var map = Map("a" -> 1, "b" -> 2, "c" -> 3)
- <- is used as a for loop index: for (i <- 1 to 3) println(i)
- => is used in lamba expressions: list.foreach {e => println(e) }
var x = 5 // Default is an Int Type var z = 2.0 // Default for a number with a decimal point is Double var y: Int = 6 // Explicit typing to an Int var a: Double = 2.0 // Explicit typing to a Double var a: Double = 2 // same var a: = 2D // Double var a: = 2d // Double var w: Int = 2.3 // error: type mismatch
val x = 5 val y: Int = 6 // Explicit typing to an Int val w: Int = 2.3 // error: tyoe mismatch val z = 2.0 // implicitly a Double val a: Double = 2.0 // Explicitly a Double a = 5.0 // error: reassignment to val
Scala encourages us to use immutable objects:
- Code is free from side-effects and is easier to reason about
- Pure functions can be tested easily
- Immutable objects won’t lead to concurrency issues
val version = util.Properties.versionNumberString // returns version: String = 2.12.4 val versionArray = version.split("\\.") // split takes a regexp so we need to escape the . // Array[String] = Array(2, 12, 4)
- Byte - 8 bit signed value. Range from -128 to 127
val b: Byte = 127 // ok val b: Byte = 0x7c // ok. b = 124 val b: Byte = 128 // error: type mismatch; val b: Byte = 0xdc // error: type mismatch; var i: Byte = _ // default initial value: Byte = 0
- Short - 16 bit signed value. Range -32768 to 32767
val s: Short = 12 // ok val s: Short = 32768 // error: type mismatch;
- Int - 32 bit signed value. Range -2147483648 to 2147483647
var x = 1 // implicit Int var x: Int = 1 var x: Int = 0xff // the value 255 in hex var i: Int = _ // default initial value: Int = 0
- Long - 64 bit signed value. -9223372036854775808 to 9223372036854775807
var a = 1L var a1: Long = 1
- Float - 32 bit IEEE 754 single-precision float
Double is inferred unless the declared variable is Float or an f or F suffix is used.
var f: Float = 3.14159f var f2: Float = 3.14159F var f3: Float = 3.1111111111111111111111111111111111 // error: type mismatch; var i: Float = _ // default initial value: Float = 0.0
- Double - 64 bit IEEE 754 double-precision float
var d = 1.0 // default implicit is a Double var d2 = 2D // a Double var d3 = 3d // a double var e: Double = 1.0 var a: Double = 1.0e100 var i: Double = _ // default initial value: Double = 0.0
- Char - 16 bit unsigned Unicode character. Range from U+0000 to U+FFFF
val a: Char = 'a' val newline: Char = '\n' val tab: Char = '\t' val quote: Char = '\"' var aunicode: Char = '\u0061' // unicode 'a'
- String - A sequence of Chars in double quotes
var s: String = "This string contains a \" character." // ok var a: String = 'asdf' // error: unclosed character literal var i: String = _ // default initial value: String = null
- Boolean - Either the literal true or the literal false
var b1 = true // implicit type var b: Boolean = true var c: Boolean = false var b: Boolean = _ // default initial value: Boolean = false
- Unit - Corresponds to no value. Like the Java void primitive.
def sayHello(): Unit = println("Hello!") // no value returned. def sayHello2: Unit = println("Hello!") // same. Note: A method without parameters can be written without parens.
- Null - null or empty reference
Null is a subtype of all reference types (i.e. any subtype of AnyRef). It has a single value identified by the keyword literal null. Null is provided mostly for interoperability with other JVM languages and should almost never be used in Scala code.
-
Nothing - The subtype of every other type; includes no values
Nothing is - together with Null - at the bottom of Scala's type hierarchy. Nothing is a subtype of every other type (including Null); there exist no instances of this type. Although type Nothing is uninhabited, it is nevertheless useful in several ways. A usage for Nothing is the return type for methods which never return normally. One example is method error in sys, which always throws an exception. Nothing can be used to signal abnormal termination (since Nothing is returned).
-
Any - The supertype of any type; any object is of type Any
Class Any is the root of the Scala class hierarchy. Every class in a Scala execution environment inherits directly or indirectly from this class. Class Any has two direct subclasses: AnyRef and AnyVal. Methods include '==', '!=', 'equals', 'hashCode' and 'toString'
-
AnyRef - The supertype of any reference type
AnyRef is the root class of all reference types. It is an alias for java.lang.Object
-
AnyVal - The supertype of any reference type
AnyVal is the root class of all primitive types.
More powerful version of a Java switch statement
import scala.util.Random val x: Int = Random.nextInt(10) x match { case 0 => "zero" case 1 => "one" case 2 => "two" case _ => "many" } // another example using a regular expression import scala.util.matching.Regex val numberPattern: Regex = "[0-9]".r numberPattern.findFirstMatchIn("awesomepassword") match { case Some(_) => println("Password OK") case None => println("Password must contain a number") } // some more examples ... (someList: List[T]) match { case Nil => ... // empty list case x :: Nil => ... // list with only one element case List(x) => ... // same as above case x :: xs => ... // a list with at least one element. x is bound to the head, // xs to the tail. xs could be Nil or some other list. case 1 :: 2 :: cs => ... // lists that starts with 1 and then 2 case (x, y) :: ps => ... // a list where the head element is a pair case _ => ... // default case if none of the above matches }
val (a, b) = (1, 2) // called an extractor of a pattern-match-expression val text = "some text goes here" val (first, rest) = if (text.contains("z")) text.splitAt(4) else text.splitAt(7) // first: String = some te // rest: String = xt goes here def someTuple = ("Al", 42, 200.0) // someTuple: (String, Int, Double) val(name, age, weight) = someTuple // name: String = Al age: Int = 42 weight: Double = 200.0
Colections can be mutable or immutable. Immutable is the default.
- Tuple
- List
- Vector
- Array
- Map
- Set
- Seq
- SortedSeq
- SortedMap
Operators are methods with one parameter used in (dot-less) operator notation. Consider the following:
// split takes 1 parameter and is used dotless "a,b,c" split "," // Array[String] = Array(a, b, c) // split called as a function. Still takes 1 parameter and a dot is used. "a,b,c".split( ",") // Array[String] = Array(a, b, c)
- Methods are just functions defined in classes
- The type should be given for public or non-trivial methods.
- Note: you should always define a return type
- A method without parameters can be written without parens. Note: No-parens style only for side-effect-free methods
def message = "Hello World!" message // returns String = Hello World! def factorial(x: BigInt): BigInt = if (x==0) 1 else x*factorial(x-1) factorial(6) // res12: BigInt = 720 // almost any name can be used as a function name. Consider the following valid function def *?!(s: String): String = s.reverse *?!( "asdf") // res21: String = fdsa def isUpcase2(s:String): Boolean = s == s.toUpperCase // isUpcase2: (s: String)Boolean isUpcase2("ASD") // Boolean = true isUpCase2("aSD") // Boolean = false
- The function type A => B is just an abbreviation for the class scala.Function1[A,B] which is roughly defined as
package scala trait Function1[A,B] { def apply(x: A): B
- Functions are objects with apply methods
- A function may be defined using a lamda expression
(x: Int) => x * x // anonymous square function (s:String) => s.toUpperCase // String => String = <function1> val anyTrue = (a:Boolean, b:Boolean) => a || b val allTrue = (a:Boolean, b:Boolean) => a && b val isUpCase = (s:String) => s == s.toUpperCase // isUpCase: String => Boolean = <function1> isUpCase("ASD") // Boolean = true isUpCase("aSD") // Boolean = false // functions taking multiple arguments def multParms(parms: Int*) = { // parms is a sequence of int, containing a varying number of arguments for (i <- parms) println(i) } multParms(5, 4, 3, 2, 1) multParms(1, 2, 3)
- Scala provides a number of different syntactic options for declaring function values. For example, the following declarations are exactly equivalent:
val f1 = ((a: Int, b: Int) => a + b) val f2 = (a: Int, b: Int) => a + b val f3 = (_: Int) + (_: Int) val f4: (Int, Int) => Int = (_ + _)
- Of these styles, the first and the last are to be preferred at all times.
- numbers may be: Short, Int, Long, Float or Double. The default is Double.
Like arrays, strings are not directly sequences, but they can be converted to them, and they also support all sequence operations on strings. Here are some examples of operations you can invoke on strings.
Alvin Alexander's String Examples
val str = "hello" str.length // Int = 5 str.reverse // String = olleh str.map(_.toUpper) // String = HELLO str.toUpperCase // String = HELLO str drop 3 // String = lo str take(2) // String = he str slice (1, 4) // String = ell " hello ".trim // String = hello str.replaceAll("l", "X") // String = heXXo str.replace('l', 'C') // String = heCCo str split("l") // Array[String] = Array(he, "", o) str.toArray // Array[Char] = Array(h, e, l, l, o) str.isEmpty // Boolean = false str.capitalize // String = Hello str.startsWith("h") // Boolean = true str.endsWith("o") // Boolean = true str.contains("ll") // Boolean = true
var x = Tuple3(1, 2, 3) // x: (Int, Int, Int) = (1,2,3) var x = (1, 2, 3) // same // define val tup = (1.0, 2, 3, "frank") // tup: (Int, Int, Int, String) = (1,2,3,frank) // access tup._1 // Double = 1.0 tup._4 // String = frank val listOfTuples = List(("a", "b", "c"), ("one", "two", "three"), ("tom", "dick", "harry")) listOfTuples.zipWithIndex foreach { case(e, i) => println(i + ": " + e) } // returns ... // 0: (a,b,c) // 1: (one,two,three) // 2: (tom,dick,harry)
- Lists are immutable. The elements of a List cannot be changed.
- Lists are recursive, while Arrays are flat.
- All Lists in Scala are constructed from the empty list Nil using the construction operations :: (cons)
1 :: List(2,3) // List[Int] = List(1, 2, 3) val nums = 1 :: (2 :: (3 :: Nil)) // List[Int] = List(1, 2, 3) nums.reduceLeft(_ + _) // (1 + 2) + 3 = 6 nums.foldLeft(0)(_ + _) // (((0 + 1) + 2) + 3) = 6 val list = List(1,2,3) // List[Int] = List(1, 2, 3) list.sum // Int = 6 var list = List("jack", 1, 3.0, Set(1,2,3)) // List[Any] = List(jack, 1, 3.0, Set(1, 2, 3)) list(0) // Any = jack list(1) // Any = 1 list(2) // Any = 3.0 list(3) // Any = Set(1, 2, 3) list(2).toString.toFloat.toInt + 12 // Int = 15 // for mutable lists use ListBuffer import scala.collection.mutable.ListBuffer var fruits = ListBuffer[String]() fruits += "Apple" // ListBuffer(Apple) fruits += "Banana" // ListBuffer(Apple, Banana) fruits += "Orange" // ListBuffer(Apple, Banana, Orange) // concatenate 2 immutable lists val a = List(1, 2) val b = List(3, 4) val c = a ::: b // List[Int] = List(1, 2, 3, 4) // prepend to an immutable list val d = 0 :: c // List[Int] = List(0, 1, 2, 3, 4) // flatten of multiple lists val names1 = List("tim", "bob", "fred", "jan") val names2 = List("sal", "jed", "sam") val names = List(names1, names2) // list of lists: List[List[String]] = List(List(tim, bob, fred, jan), List(sal, jed, sam)) names.flatten // hierarchy flattened: List[String] = List(tim, bob, fred, jan, sal, jed, sam)
val map = Map("a" -> 1, "b" -> 2) // Map[String,Int] = Map(a -> 1, b -> 2) val map = Map(234 -> 1, "b" -> 2) // Map[Any,Int] = Map(234 -> 1, b -> 2) // adding to a map var map = Map(234 -> 1, "b" -> 2) map += ("a" -> 728)
- A Range is a sequence of evenly spaced integers.
- The Range class represents integer values in range [start;end) with non-zero step value step. It's a special case of an indexed sequence. For example:
val r1 = 0 until 10 // Range(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) val r2 = r1.start until r1.end by r1.step + 1 // Range(0, 2, 4, 6, 8) val x = Range(1, 5) // Range(1, 2, 3, 4) val odds = Range(1, 10, 2) // Range(1, 3, 5, 7, 9) val odds = 1 to 10 by 2 // exactly same odds.end // Int 10 odds.last // Int 9 val evens = Range(0, 10, 2) // Range(0, 2, 4, 6, 8) var evens = 0 to 10 by 2 // exactly same for (i <- evens) println(i) // prints 0 2 4 6 and 8 evens.start // Int = 0 evens.end // Int = 10 evens.step // Int = 2 var numbers = 1 to 10 // Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) // using a range numbers.filter{_ > 5} // Vector(6, 7, 8, 9, 10) numbers filter{_ > 5} // same numbers.filter{_ % 2 == 0} // IndexedSeq[Int] = Vector(2, 4, 6, 8, 10) numbers.filter(e => e > 5) // Vector(6, 7, 8, 9, 10) numbers filter(e => e > 5) // same using no dot numbers filter{e => e > 5} // same using curly braces numbers filter{_ > 5} // same using anonymous var _ numbers filter(_ > 5) // same with parens
- Sets are unordered. They do not have a predefined order.
- Sets do not have duplicate elements
- The fundamental operation on Sets is contains
val i = Set(1,2,3) // Set[Int] = Set(1, 2, 3) Set(1,2) subsetOf i // Boolean = true var set = Set(1,2,2,3,4,5,1,2) // dupes removed ... Set(5, 1, 2, 3, 4) set += 100 // Set[Int] = Set(5, 1, 2, 3, 4, 100) set -= 5 // Set[Int] = Set(1, 2, 3, 4, 100) set += (10,11) // Set[Int] = Set(10, 1, 2, 3, 11, 4, 100) set -= (1,2,3,4) // Set[Int] = Set(10, 11, 100) set.sum // Int = 121 // is element in set? set(100) // Boolean = true set contains 100 // same as above set.contains(100) // same as above set(20) // Boolean = false // add another set set ++ Set(20,30,40) // Set[Int] = Set(10, 20, 11, 40, 30, 100) // flatten of multiple sets val names1 = Set("tim", "bob", "fred", "jan") val names2 = Set("sal", "jed", "sam") val names = Set(names1, names2) // set of sets: Set[Set[String]] = Set(Set(tim, bob, fred, jan), Set(sal, jed, sam)) names.flatten // hierarchy flattened: Set[String] = Set(sal, fred, tim, sam, bob, jed, jan)
var numbers = 1 to 3 // scala.collection.immutable.Range.Inclusive = Range(1, 2, 3) // lamda style numbers map(x => x + 1) // scala.collection.immutable.IndexedSeq[Int] = Vector(2, 3, 4) numbers map(_ + 1) // scala style. same. numbers filter (_ < 3) // scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2) numbers sortWith ((x, y) => x > y) // scala.collection.immutable.IndexedSeq[Int] = Vector(3, 2, 1) numbers map (_ + 1) sortWith (_ > _) // scala.collection.immutable.IndexedSeq[Int] = Vector(4, 3, 2)
15.toFloat // Float = 15.0 15.0.toInt // Int = 15 15f.toDouble // Double = 15.0 15L.toShort // Short = 15 15.123d.toLong // Long = 15 "15.2".toFloat // Float = 15.2 (1 to 3).toSet // Set[Int] = Set(1, 2, 3) (1 to 3).toList // List[Int] = List(1, 2, 3) (1 to 6).toArray // Array[Int] = Array(1, 2, 3, 4, 5, 6)
var x = 10 if (x < 20) { println("This is if statement") } if (x == 10){ println("x is 10") } else { println("x is not 10") } if (x == 10) println("x is 10") else println("x is not 10")
var x = 10 if (x == 10) { println("x is 10") } else if (x > 10 && x <= 20) { println("x is between 10 and 20") } else if (x > 20 && x <= 1000) { println("x is between 20 and 1000") } else { println("x is greater than 1000 or less than 10") }
val names = Seq("chris", "ed", "maurice") val nums = Seq(1, 2, 3) val letters = Seq('a', 'b', 'c') for (n <- names) println(n) for (n <- names) println(n.capitalize) for (n <- names) { // imagine this requires several lines println(n.capitalize) } for (i <- 1 to 3) println(i) // generator in for loop val res = for { n <- nums c <- letters } yield (n, c) // Seq[(Int, Char)] = List((1,a), (1,b), (1,c), (2,a), (2,b), (2,c), (3,a), (3,b), (3,c)) for (i <- 1 to 10 if i < 4) println(i) // generator with one guard for (i <- 1 to 10 if i > 3 if i < 6 if i % 2 == 0) println(i) // ... with multiple guards val names2 = for (e <- names) yield e.capitalize // yield creates a new data structure val out = for (e <- names) yield e.capitalize val out = names map(_.capitalize) val nameMap = Map("firstName" -> "Ed", "lastName" -> "Chigliak") val result = for ((k,v) <- nameMap) yield { s"key: $k, value: $v" } // List(key: firstName, value: Ed, key: lastName, value: Chigliak) names.foreach(println) names.foreach(e => println(e.toUpperCase)) names.foreach { // imagine this requires multiple lines e => println(e.toUpperCase) } // Example to find all the possible divisors of n (excluding 2) import scala.math val n = 101 val divisors = for (i <- 3 to n by 2 if i < Math.sqrt(n)) yield(i) // Vector(3, 5, 7, 9) // which of the above divisors evenly divides into 1001 val divisableBy = for (e <- divisors) yield 1001 % e == 0 // is this number prime? val isPrime = ! divisableBy.reduceLeft((a:Boolean, b:Boolean) => a || b)
val args = List("first", "second", "third") args.foreach((i: String) => println(i)) // passing a function to foreach args.foreach(i => println(i)) // more concise, type of the arg is inferred args.foreach(println(_)) // even more consise args.foreach(println) // no need to pass an arg since println takes only 1 arg
- Classes have public visibility by default
// define class Person(var name: String, var age: Int) // use val person = new Person("John Smith", 15) // getters person.name person.age // setters person.name = "Fred Brown" person.age = 23 // another way to define a class. This one has a default constructor. Note: the default constructor is all of the expressions in the // class outside of the methods. These are automatically collected into a default constructor. class Person() { println("creating instance of Person ...") var name = "" // code you put into the class becomes part of the "primary" constructor var age = 0 // ditto // auxilliary constructor. Must call this def this(name: String) = { this } }
Implicit Method Examples and Syntax
- allows adding methods to an object without modifying the source code of the object
- also commonly known as extension methods
- allows for the creation of infix methods
- They must be defined inside of another trail, class or object
- They may only take one non-implicit argument in their constructor
- There may not be any method, member or object in scope with the same name as the implicit class
object Helpers { implicit class RichInt(val x: Int) { def isAFactorOf(y: Int) = y % x == 0 } implicit class StringUtil(val s: String) { def takeN(len: Int) = if (len > 0 && s != null && s.length > len) s.take(len) else s } } import Helpers._ 2.isAFactorOf(10) // true 2 isAFactorOf 10 // true 2 isAFactorOf 12 // true 2 isAFactorOf 13 // false "abcd" takeN 2 // String = "ab" "abcd" take 50 // String = "abcd" val s: String = null s takeN 2 // String = null s take 2 // null pointer exception
- A case class generates a lot of code for you with the following benefits:
- An apply method so you don't need to use the new keyword to create a new instance of the class
- Accessor methods are generated for each case class constructor parameter. All are public val fields by default
- mutator methods would also be generated for all constructor parameters declared as var
- An unapply method is generated which makes it easy to use case classes in match expressions
- A copy method is generated
- equals and hashcode methods are generated which lets you compare objects and easily use them as keys in maps and sets
- a default toString method is generated which is helpful for debugging
case class Person(name: String, relation: String) val frank = Person("Frank Brown", "uncle") // no need for new keyword frank // prints Person(Frank Brown,uncle) frank.name // String = Frank Brown frank match { case Person(n, r) => println(n, r) } // (Frank Brown,uncle) val unc = Person("Frank Brown", "uncle") unc == frank // true frank.hashCode // Int = 1926139159 unc.hashCode // same Int = 1926139159 // Let's try with a non-case class so we can plainly see the differences since we did not get any generated methods: class Person2(name: String, relation: String) val jane = new Person2("Jane Smith", "aunt") // must use keyword new to create an instance jane // no default toString method. instead we get Person2@75d454a4 jane.name // no accessors instead we get error: value name is not a member of Person2 jane match { case Person(n, r) => println(n, r) } // error: constructor cannot be instantiated to expected type; val aunt = new Person2("Jane Smith", "aunt") jane == aunt // false, since we have no equals or hashcode jane.hashCode // Int = 1976849572 aunt.hashCode // different Int = 555428396
- A singletion is an oject which can only have one instance. No other instances are allowed
- In Scala we use the object key word to define a singleton. It is part of the language specification.
- In Scala if both a class and an object have the the same name, they are called companions and must reside in the same file.
// singleton definition object Car { def f() = { println("in f") } } // usage Car.f // prints "in f"
- Enumerations are a language feature specially useful for modeling a finite set of entities.
- Scala, as many other languages, provides a native way to represent enumerations
- For a more thorough examination of the subject see Enumerations
- The recommendation from the above blog post is to use the enumeratum library see
object Weekday extends Enumeration { val Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday = Value } Weekday.withName("Monday") // Weekday.Value = Monday Weekday.withName("Mondai") // NoSuchElementException: No value found for 'Mondai' Weekday.values // Weekday.ValueSet = Weekday.ValueSet(Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
// definition def total(list: List[Int]) = { var sum = 0 for (i <- list) { sum += i } sum } // usage total(List(1,2,3,4,5)) // Int = 15
- We want to promote immutability
- Assignmentless programming
- Functions don't have side-effects
def total1(list: List[Int]) = { var sum = 0 list.foreach { e => sum += e} sum } total1(List(1,2,3,4,5)) // Int = 15 def total2(list: List[Int]) = { var sum = 0 list.foreach { sum += _ } sum } total2(List(1,2,3,4,5)) // Int = 15 def total3(list: List[Int], selector: Int => Boolean) = { var sum = 0 list.foreach { e => if (selector(e)) sum += e } sum } total3(List(1,2,3,4,5), { e=> true}) // all values ... Int = 15 total3(List(1,2,3,4,5), { e=> e%2 == 0}) // even values ... Int = 6 total3(List(1,2,3,4,5), { e=> e > 3}) // values > 3 ... Int 9 total3(List(1,2,3,4,5), { _ <= 3}) // values <= 3 ... Int 6 def total4(list: List[Int]) = { list.foldLeft(0) { (carryOver, e) => carryOver + e } } total4(List(1,2,3,4,5)) // Int = 15
- Traits resemble interfaces in Java. They can contain both abstract and concrete methods.
- Traits differ from classes in that traits cannot have (value) parameters. Only classes can have parameters.
- Traits can be used in several ways which are shown below. First, they can extends a class. Secondly, they can be mixed in with a class definition using the with keyword. Lastly, they can be mixed in to an object definition using the with keyword.
trait Friend { val name: String def listen = println("I am "+name+" your friend") } // first, a class can be extended by a trait class Human(val name: String) extends Friend val peter = new Human("Peter") peter.listen // I am Peter your friend // secondly, a class can mixin a trait class Dog(override val name: String) extends Animal(name) with Friend val rover = new Dog("Rover") rover.listen // I am Rover your friend // lastly, an object can mixin a trait. Note: No trait class Cat(override val name: String) extends Animal(name) // mixin trait with the object definition val fluffy = new Cat("Fluffy") with Friend fluffy.listen // I am Fluffy your friend
- Unlike private and protected members, it is not required to specify Public keyword for Public members.
import scala.math Math.sqrt(25.0) // Double = 5.0 Math.PI // Double = 3.141592653589793 Math.tan(Math.PI * 2) // Double = -2.4492935982947064E-16 ... almost 0
- Scala allows a type alias definition for custom types we wish to give name to.
// suppose we have a custom type which is a List of Int and Double pairs (tuples) val list = List( (0 ,0.0), (1, 1.1), (2, 2.2)) // List[(Int, Double)] = List((0,0.0), (1,1.1), (2,2.2)) // we can define a custom type thusly type MyNumberPairs = List[(Int, Double)] // defined type alias MyNumberPairs // usage val x: MyNumberPairs = List( (0 ,0.0), (1, 1.1), (2, 2.2)) // MyNumberPairs = List((0,0.0), (1,1.1), (2,2.2)) val x: MyNumberPairs = List( (0 , 0)) // ok. x: MyNumberPairs = List((0,0.0)) val x: MyNumberPairs = List( (0.1, 0)) // will be an error. error: type mismatch;
import java.io.FileReader import java.io.FileNotFoundException import java.io.IOException try { val f = new FileReader("input.txt") } catch { case ex: FileNotFoundException => { println("Missing file exception") } case ex: IOException => { println("IO Exception") } }