Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Masterseminar Scala
Fabian Becker
FH Giessen-Friedberg
15. November 2010
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Content
1 Packages & Imports
2 Assertions and Unit Testing
3 Case Classes and Pattern Matching
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Packages
Packages
Important to minimize coupling
Scala code resides in Java global hierarchy of packages
Per default code is in the unnamed package
There are two ways to place code inside packages
Listing 1.1
package database.mysql
class Query
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Packages
Listing 1.2
package database {
package mysql {
class Query
}
}
Listing 1.3
package database.mysql {
class Query
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Packages
Packages
Packages in Scala truly nest
In Java packages are in a hierarchy they don’t nest
Naming a package in Java you always start at the root
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Packages
Listing 1.4: Accessing packages
package game {
package ui {
class Player
}
package engine {
class Main {
// Java: new game.ui.Player()
val player = new ui.Player
}
}
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Packages
Listing 1.5: More complex
package ui { class Player } // In ui.scala
package game { // In game.scala
package ui { class Player }
package engine {
package ui { class Player }
class Main {
val player = new ui.Player
val player2 = new game.ui.Player
val player3 = new _root_.ui.Player
}
}
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Imports
Importing packages
Importing packages allows to directly access members in thatpackage
Scala imports are similar to the imports from Java
Imports can appear anywhere
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Imports
Listing 1.6.1: Defining Enemies
package game
abstract class Enemy(
val name: String,
var lvl: Int
)
object Enemies {
object Dwarf extends Enemy("Gimli", 10)
object Orc extends Enemy("Bwarg", 5)
object Necromancer extends Enemy("Destructor", 99)
val col = List(Dwarf, Orc, Necromancer)
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Imports
Listing 1.6.2: Importing Enemies
// Simple import for Enemy
import game.Enemy
// Import all members of game
import game._
// Import all members of Enemies
import game.Enemies._
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Imports
Listing 1.7: Import in a function
def showEnemy(enemy: Enemy) {
import enemy._
println("Enemy - Name:"+name+" Level:"+lvl)
}
Imports
Imports can appear in functions
They import all members of a object
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Selective Imports
Listing 1.8: Selective Imports
// Only import Dwarf and Orc
import game.Enemies.{Dwarf, Orc}
// Renaming an import
import game.Enemies.{Necromancer => Necro}
// Rename one, import the rest
import game.Enemies.{Orc => O, _}
// Exclude an import
import game.Enemies.{Orc => _, _}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Selective Imports
Selective Imports
Import: import game.Enemies.{Orc} ⇔ game.Enemies.Orc
Rename: 〈originalname〉 ⇒ 〈newname〉Hide Packages: 〈original − name〉 ⇒Catch All: importgame.Enemies.{ } ⇔ game.Enemies.
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Implicit imports
Listing 1.9: Implicit imports
import java.lang._ // everything in the java.lang package
import scala._ // everything in the scala package
import Predef._ // everything in the Predef object
Implicit imports
Packages imported implicitly by Scala
scala. overshadows java.lang.
scala.StringBuilder ⇒ java.lang.StringBuilder
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Access modifiers
Listing 1.10: Private modifiers
class Outer {
class Inner {
private def f() { println("f") }
class MostInner { f(); // OK }
}
(new Inner).f() // fails
}
Private
Scala: private members are only accessible inside the class orobject that contain their definition
Java allows outer classes to access private members of theirinner classes
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Access modifiers
Protected
Java: protected members are accessible within the samepackage
Scala: Only from the object they are defined in and theirsubclasses
Public
There is no explicit modifier for public
All members not labeled private or protected are public
Accessable from anywhere
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Access modifiers
Scope of protection
Scala allows to control access to a level X
private[X] and protected[X] allow access up to X
Listing 1.11: Example
package game {
package ui {
private[game] class Player {
private[this] var hp = 42
protected[ui] def getHP() { hp }
}
}
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Assertions and Unit Testing
Testing Code
Assertions for run-time checking
Unit testing for development testing of small units
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Assertions
Listing 2.1: Assert
def add1(x: Int) : Int = {
val z = x + 1
assert(z > x)
return z
}
println(add1(41))
Assertion methods
Method assert predefined in Predef
assert(condition)
assert(condition, explanations)
If the condition doesn’t hold, an AssertionError is thrown
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Assertions
Listing 1.13: Ensuring
def lvlUp(e: Enemy): Enemy = {
if(e.lvl < 100) {
e.lvl = e.lvl + 1
} ensuring(e.lvl < 100)
e
}
Ensuring
Method assert predefined in Predef
assert(condition)
assert(condition, explanations)
If the condition doesn’t hold, an AssertionError is thrown
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Unit testing
ScalaTest
org.scalatest.Suite (http://www.scalatest.org/)
Can be executed on the Scala console: (newHitchhikerSuite).execute()
Listing 2.3: Simple testcase
import org.scalatest.Suite
class HitchhikerSuite extends Suite {
def testAnswerToLife() {
val answer = 42
assert(answer == 42)
}
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Unit testing
ScalaTest with Fun
ScalaTest allows different styles of testing
FunSuite is a trait that overrides execute and allows to definetests as function values
Naming the tests is easier (you can use Strings)
Listing 2.4: FunSuite
import org.scalatest.FunSuite
class HitchhikerSuite extends FunSuie {
test("The answer to life should be 42") {
val answer = 42
assert(answer == 42)
}
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Unit testing
ScalaTest - Informative testing
A normal assertion produces long error messages but do notshow why the match failed
ScalaTest defines the === operator for assert(), shows thevalues
Alternative: expect(value) { block }
Listing 2.5: Informative testing
assert(answer === 42)
// or
expect(42) {
answer
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Unit testing
ScalaTest - Catching Exceptions
Testing for Exceptions can be done with intercept()
Listing 2.6: Intercept
s = "hi"
intercept[IndexOutOfBoundsException] {
s.charAt(-1)
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Unit testing
JUnit
Most popular test framework for Java
Can easily be used from Scala
Does not natively support Scala’s assertion syntax
Listing 2.7: JUnit Testcase
import junit.framework.TestCase
import junit.framework.Assert.assertEquals
import junit.framework.Assert.fail
class MyTestCase extends TestCase {
def testAnswer() {
val answer = 42
assertEquals(42, answer)
}
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Unit testing
JUnit + Scala
ScalaTest defines JUnit4Suite (requires JUnit 4 of course)
JUnit4Suite extends TestCase
Listing 2.8: Using Scala syntax in JUnit
import org.scalatest.junit.JUnit4Suite
class AnswerSuite extends JUnit4Suite {
def testAnswerToLife() {
var answer = 42
assert(answer === 42)
}
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Unit testing
TestNG
TestNG framework inspired by JUnit/nUnit
Uses annotations
Listing 2.9: TestNG
import org.testng.annotations.Test
import org.testng.Assert.assertEquals
class AnswerTests {
@Test def verifyAnswerToLife() {
val answer = 42
assertEquals(answer, 42)
}
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Unit testing
TestNG + Scala
ScalaTest defines trait TestNGSuite
TestNGWrapperSuite enables TestNG to test Scala while thetests are written in Java
Listing 2.10: TestNG + Scala
import org.scalatest.testng.TestNGSuite
import org.testng.annotations.Test
class AnswerSuite extends TestNGSuite {
@Test def verifyAnswerToLife() {
val answer = 42
expect(42) { answer }
assert(answer === 42)
}
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Unit testing
Listing 2.11: TestNG + Scala
import org.scalatest.Spec
class AnswerSpec extends Spec {
"The Answer" -- {
"should be an integer" - {
val answer = 42
assert(answer.isInstanceOf[Int] == true)
}
"should be 42" - {
val answer = 42
assert(answer === 42)
}
}
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Unit testing
Tests as specification
Behavior-driven-development (BDD) lays emphasis onhuman-readable testing
ScalaTest includes a trait Spec
Spec contains describers and specifiers
Each specifier will be run as a seperate ScalaTest
Output (new AnswerSpec).execute()
The answer- should be an integer- should be 42
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Case Classes and Pattern Matching
Tests as specification
Case Classes
Pattern Matching
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Case classes
Listing 3.1: Definitions
abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String,left: Expr, right:
Expr) extends Expr
The case modifier
Example from the book
The case modifier adds syntactic conveniences
Factory method: val v = Val(”x”)
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Case classes
Listing 3.2: Nesting
val op = BinOp("+", Number(42), Var("x"))
The case modifier
Allows to ommit the new
All constructor parameters implicitly get a val prefix
toString, equals, hashCode are automagically added
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Listing 3.3: Matching
def simplifyTop(expr: Expr): Expr = expr match {
case UnOp("-",UnOp("-",e)) => e // Double negation
case BinOp("+", e, Number(0)) => e // Adding zero
case BinOp("*", e, Number(1)) => e // Multiplying by one
case _ => expr
}
Matching
Similar to Javas switch
Syntax: selector match {alternatives}match is an expression and results in a value
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Patterns
e, variable pattern
, wildcard pattern, drops the value
UnOp(“-“, e), constructor pattern, matches values of typeUnOp with - as first param.
If no pattern matches a MatchError is thrown.
Listing 3.4: Matching
expr match {
case BinOp(_, _, _) => println(expr +"is a binary
operation")
case _ => println("It’s something else")
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Constant patterns
Any literal (String, Boolean, Integer) can be used as a pattern
Literals match themselves
Listing 3.5: Constant patterns
def inspect(x: Any) = x match {
case 12 => "twelve"
case Nil => "nothing"
case false => "negative"
case _ => "Cant’t say"
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Variable patterns
Variable pattern matches like a wildcard, but the result getsassigned to the variable
Listing 3.6: Variable patterns
expr match {
case 12 => "Not right"
case number => "This is a number " + number
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
How does Scala tell them apart?
Scala uses a lexical rule to differ between variable andconstant patterns
Lowercase identifiers are treated as variable
Uppercase identifiers are assumed to be a constant
Exception to the rule?
true and false
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Constructor patterns
Check against a constructor and it’s parameters
Object gets inspected
Contents of the object are checked aswell
Listing 3.7: Constructor pattern
expr match {
case BinOp("-", m, Number(12)) => println(m + " - 12")
case _ =>
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Sequence patterns
Match against sequence types like List, Array, ...
Allows to specify the number of elements in the pattern
⇔ Wildcard, * ⇔ 0..n elements
Listing 3.8: Sequence pattern with fixed length
expr match {
case List(n, _, _) => println("Top-Element: " + n)
case List(n, _*) => println("Top-Element: " + n)
case _ =>
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Tuple patterns
A pattern like (x, y) matches a 2-tuple
Listing 3.9: Tuple pattern
expr match {
case (x, y) => println("Matched a 2-tuple")
case _ =>
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Typed patterns
Alternative for type checks and type casts
Type check: expr.instanceOf[String]
Type cast: expr.asInstanceOf[String]
Listing 3.10: Typed pattern
def getSize(e: Any) = e match {
case s: String => s.length
case m: Map[_,_] => m.size
case _ => -1
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Type erasure
Matching a Map with special element types doesn’t work!
Scala/Java use the erasure model for generics
No information about type arguments is maintained atrun-time
Arrays are an exception, they are handled differently
Listing 3.11: Type erasure - Matches all Maps!
expr match {
case m: Map[Int, String] => println("String-Int Map")
case _ =>
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Variable binding
Perform the normal match but bind the match to a variable
The @ sign binds a variable to a match
Listing 3.12: Binding a variable to a match
expr match {
case UnOp("abs", e @ UnOp("abs", _)) => e
case _ =>
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Pattern guards
Sometimes a match can have pre-conditions
The conditions are “guarding“ the match
Listing 3.13: Match with pre-condition
expr match {
case BinOp("/", _, y) if y == 0 => println("Nominator
can’t be 0")
case _ =>
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Pattern overlaps
Patterns are tried in the order they’re written
Changing the order of cases can change behaviour
Listing 3.14: Simple pattern overlap
expr match {
case BinOp("+", e, Number(10)) => println("BinOp + 10")
case BinOp("-", e, Number(10)) => println("BinOp - 10")
case BinOp(op, e, Number(10)) => println("BinOp "+op+"
10")
case _ => expr
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Sealed classes
When you match using patterns, how do you make sure youmatched everything?
sealed classes allow the compiler to know all possible cases
A sealed class can only have subclasses in the same file
The compiler can then generate warnings (match notexhaustive)
Listing 3.15: Sealed class
sealed abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Sealed classes
If you leave out an option a warning is thrown: “warning:match is not exhaustive! missing combination Type“
Listing 3.16: Match on a sealed class
expr match {
case Var(x) => "Variable " + x
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Sealed classes
Warning can be avoided by doing a wildcard match
Suppressed using an annotation (will be covered in Chapter25)
Usefulness?
Why make the class sealed in the first place?
Listing 3.17: Match on a sealed class
(expr: @unchecked) match {
case Var(_) => "a variable"
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
The Option type
Standard type for optional values
Can be Some(x) or None
Is returned by HashMap, Map, ..
None is the Scala way of returning null
Listing 3.18: Unpacking an option
val map = Map(1 -> "a", 2 -> "b", 3 -> "c")
(map get 1) match {
case Some(s) => println(s)
case None => println("Not there")
}
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Patterns in other places
Patterns can be used almost anywhere
Listing 3.19: Other uses of patterns
val (a, b) = (1, 2)
for((num, letter) <- Map(1->"a", 2->"b"))
println("Num:"+num+" Letter:"+letter)
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Case sequences as partial functions
Case sequences can be used partial functions
Partial functions throw a RuntimeException if they are appliedagainst a value they don’t support
Compiler can throw warnings
Listing 3.20: Case sequence as partial function
val myMatch: Option[Int] => String = {
case Some(x) => "Value: " + x
case None => "Nothing there"
}
myMatch(Some(12))
// => Value: 12
Packages & Imports Assertions and Unit Testing Case Classes and Pattern Matching
Pattern matching
Case sequences as partial functions
By telling the compiler that you use a partial function you cancheck whether it’s possible to apply a value
Listing 3.21:
val listMatch: PartialFunction[List[Int], Int] = {
case x :: y :: _ => y
}
// Can we apply the List?
listMatch.isDefinedAt(List(1,2,3))
// => true