Scala for Java Developers © 2011 SpringOne 2GX. All rights reserved. Do not distribute without permission.
May 10, 2015
Scala for Java Developers
© 2011 SpringOne 2GX. All rights reserved. Do not distribute without permission.
What is Scala
2
“a general purpose programming language designed to express common programming patterns in a concise, elegant, and type-safe way. It smoothly integrates features of object-oriented and functional languages, enabling Java and other programmers to be more productive.”
http://www.scala-lang.org
Object-oriented
• Everything is an object– No “primitives”
• Classes– Same as Java, but concise
• Traits– Interfaces done right
• Singletons– Language-level concept
3
Statically typed
• Rich type system (by Java’s standard)– Higher-kinded types
– Implicit conversions
– Type evidence
• Expressive type system• Inferred types
4
Functional Programming
• Functions as values– May be
• Saved• Passed to other functions (higher-order functions)
No need to write ugly anonymous classes
– Advanced pattern matching
– Expressions return a value• if/else, try/catch, match, …
• Promotes immutability– But Scala doesn’t force it
5
Java Interoperability
• Compiles to Java byte code– Jars, wars, …
– No special operational change
• Scala calling Java, Java calling Scala code is fine• Whole Java eco-system at your service
• You can write apps using Scala and Spring, today
6
Hello World: Scripting Style
$ scala hello-script.scalaHello World$ scala hello-script.scalaHello World
No compilationNo compilation
Hello World: Porting of Java Code
$ scalac hello-java.scala$ scala example.MainHello World
$ scalac hello-java.scala$ scala example.MainHello World
‘static’‘static’
Inferred semicolons
Inferred semicolons
Hello World: Using the App trait
$ scalac hello-app.scala$ scala example.MainHello World
$ scalac hello-app.scala$ scala example.MainHello World
Simple Class
class Personclass Person
val p = new Person val p = new Person
Type Inferred
Type Inferred
Default access: public
Default access: public
No curly braces needed (but allowed)
No curly braces needed (but allowed)
Simple Class
class Personclass Person
val p: Person = new Person val p: Person = new Person
Explicit type specificationExplicit type specification
Class with constructor
class Person(firstName: String, lastName: String)class Person(firstName: String, lastName: String)
val p = new Person("Ramnivas", "Laddad") println(p.firstName) // Error val p = new Person("Ramnivas", "Laddad") println(p.firstName) // Error
Primary constructor
Primary constructor
Fields – accessible in class body
Fields – accessible in class body
Class with “getters”
class Person(val firstName: String, val lastName: String)class Person(val firstName: String, val lastName: String)
val p = new Person("Ramnivas", "Laddad") println(p.firstName) val p = new Person("Ramnivas", "Laddad") println(p.firstName)
Value (Java ‘final’)
Value (Java ‘final’)
Class with “getters” and “setters”
class Person(var firstName: String, var lastName: String)class Person(var firstName: String, var lastName: String)
val p = new Person("Ramnivas", "Laddad")println(p.firstName)p.firstName = "Ramnivas2”
val p = new Person("Ramnivas", "Laddad")println(p.firstName)p.firstName = "Ramnivas2”
Variable (Java non-final)
Variable (Java non-final)
Extending a class
val s = new Student("Ramnivas", "Laddad", 1)println(s.firstName)println(s.grade)
val s = new Student("Ramnivas", "Laddad", 1)println(s.firstName)println(s.grade)
Defining methods
class Person(val firstName: String, val lastName: String) { def name = firstName + " " + lastName
override def toString = name }
class Person(val firstName: String, val lastName: String) { def name = firstName + " " + lastName
override def toString = name }
val p = new Person("Ramnivas", "Laddad")println(p.name) // Ramnivas Laddadprintln(p) // Ramnivas Laddad
val p = new Person("Ramnivas", "Laddad")println(p.name) // Ramnivas Laddadprintln(p) // Ramnivas Laddad
Not optionalNot optional
Uniform access principle
class Person(val firstName: String, val lastName: String) { val name = firstName + " " + lastName
override def toString = name }
class Person(val firstName: String, val lastName: String) { val name = firstName + " " + lastName
override def toString = name }
val p = new Person("Ramnivas", "Laddad")println(p.name) // Ramnivas Laddadprintln(p) // Ramnivas Laddad
val p = new Person("Ramnivas", "Laddad")println(p.name) // Ramnivas Laddadprintln(p) // Ramnivas Laddad
Names in Scala
• Class, method, field names can contain non alpha-numeric characters– ::
– :::
– ~>
– f@#:
• Valuable if used judiciously– DSLs
18
More about methods and fields
• Declaring abstract methods and fields– Just don’t provide definition
def learn(subject: String) val knowledge
• Classes with abstract method must be declared abstract– Just as in Java
• Methods can be defined inside methods• Methods may be marked @tailrec to check for tail
recursiveness
19
Finer access control levels
• Default access level: public• Protected: protected
– Same as Java
• Private:– private – private[this] Access only from this instance – private[package-name] Access from package and its
subpackages
20
Traits: Interfaces done right
21
trait PartyGoer { val age: Int val yearsUntilLegalDrinking = if (age >= 18) 0 else 18-age}
trait PartyGoer { val age: Int val yearsUntilLegalDrinking = if (age >= 18) 0 else 18-age}
Collections
val people = List("John", "Jacob", "Mike")val people = List("John", "Jacob", "Mike")
Collections
val people = Array("John", "Jacob", "Mike")val people = Array("John", "Jacob", "Mike")
Working with collections: for comprehension
24
for (person <- people) { println(person)}
for (person <- people) { println(person)}
Working with collections: for comprehension
25
for (person <- people if person startsWith "J") { println("""Lucky one to start name in "J" """ + person)}
// You are lucky one to start name in "J" John// You are lucky one to start name in "J" Jacob
for (person <- people if person startsWith "J") { println("""Lucky one to start name in "J" """ + person)}
// You are lucky one to start name in "J" John// You are lucky one to start name in "J" Jacob
Working with collections: filter
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
Working with collections: filter
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
Working with collections: using _
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
Working with collections: passing method as function
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
Working with collections: partition
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val (elementarySchoolers, middleSchoolers) = students.partition(_.grade < 6)println(elementarySchoolers)println(middleSchoolers)//List(first1 last1, first2 last2, first3 last3)//List(first4 last4)
val (elementarySchoolers, middleSchoolers) = students.partition(_.grade < 6)println(elementarySchoolers)println(middleSchoolers)//List(first1 last1, first2 last2, first3 last3)//List(first4 last4)
TupleTuple
Working with collections: transforming
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
val student1 = new Student("first1", "last1", 1)val student2 = new Student("first2", "last2", 1)val student3 = new Student("first3", "last3", 2)val student4 = new Student("first4", "last4", 6)val students = List(student1, student2, student3, student4)
Map
println(studentSchools(student1)) // Millerprintln(studentSchools(student1)) // Miller
More collections
• Set• IndexedSeq• Vector (good one!)• Range
– 1 to 100 – 1 until 100– 2 until 100 by 2
• …
• Mutable versions• Parallel versions
33
Putting it together: Quicksort
def quicksort[T](input: Traversable[T]) (ordering: Ordering[T]) : Traversable[T] = if (input.isEmpty) { input } else { val (low, high) = input.tail.partition(ordering.lt(_, input.head)) quicksort(low)(ordering) ++ List(input.head) ++ quicksort(high)(ordering) }
def quicksort[T](input: Traversable[T]) (ordering: Ordering[T]) : Traversable[T] = if (input.isEmpty) { input } else { val (low, high) = input.tail.partition(ordering.lt(_, input.head)) quicksort(low)(ordering) ++ List(input.head) ++ quicksort(high)(ordering) }
println(quicksort(List(1, 3, 4, 5, 1))(Ordering.Int))println(quicksort(List(1, 3, 4, 5, 1))(Ordering.Int))
Putting it together: Quicksort
println(quicksort(List(1, 3, 4, 5, 1)))println(quicksort(List(1, 3, 4, 5, 1)))
Pattern matching: Basics
val a:Any = "foo" a match { case str: String => println("A string: " + str) case i: Int => println("An int: " + i) case _ => println("Something else")}
val a:Any = "foo" a match { case str: String => println("A string: " + str) case i: Int => println("An int: " + i) case _ => println("Something else")}
Pattern matching: with collections
val l = List("a", "b", "c") l match { case Nil => println("Empty!") case head :: Nil => println("Only one item " + head) case head :: tail => println("Item " + head + " followed by " + tail) }
val l = List("a", "b", "c") l match { case Nil => println("Empty!") case head :: Nil => println("Only one item " + head) case head :: tail => println("Item " + head + " followed by " + tail) }
Quicksort with pattern matching
def quicksort[T](input: Traversable[T]) (implicit ordering: Ordering[T]) : Traversable[T] = input match { case head :: tail => val (low, high) = tail.partition(ordering.lt(_, head)) quicksort(low) ++ List(head) ++ quicksort(high) case _ => input }
def quicksort[T](input: Traversable[T]) (implicit ordering: Ordering[T]) : Traversable[T] = input match { case head :: tail => val (low, high) = tail.partition(ordering.lt(_, head)) quicksort(low) ++ List(head) ++ quicksort(high) case _ => input }
println(quicksort(List(1, 3, 4, 5, 1)))println(quicksort(List(1, 3, 4, 5, 1)))
Destutter using Pattern matching
39
def destutter[A](lst: List[A]): List[A] = lst match { case h1 :: h2 :: tail if (h1 == h2) => destutter(h2 :: tail) case h1 :: h2 :: tail => h1 :: destutter(h2 :: tail) case _ => lst}
def destutter[A](lst: List[A]): List[A] = lst match { case h1 :: h2 :: tail if (h1 == h2) => destutter(h2 :: tail) case h1 :: h2 :: tail => h1 :: destutter(h2 :: tail) case _ => lst}
// destutter(List(1,1,1,1,1,1)) => List(1)// destutter(List(1,1,4,3,3,2)) => List(1,4,3,2)// destutter(List() )=> List()
// destutter(List(1,1,1,1,1,1)) => List(1)// destutter(List(1,1,4,3,3,2)) => List(1,4,3,2)// destutter(List() )=> List()
Case classes
• Useful in pattern matching– “case”
• Offer many useful common methods– equals()
– hashCode()
– toString
– copy()
40
Case classes
41
case class Human(name: String)case class SuperHero(name: String, power: String)case class Human(name: String)case class SuperHero(name: String, power: String)
val characters = List(Human("Programmer"), SuperHero("Customer", "money"), SuperHero("QA", "testing"))
val characters = List(Human("Programmer"), SuperHero("Customer", "money"), SuperHero("QA", "testing"))
Case classes and pattern matching
42
val actions = for (character <- characters) yield character match { case Human(name) => name + " needs to be saved" case SuperHero(name, power) => name + " will save using " + power }
actions.foreach(println)
val actions = for (character <- characters) yield character match { case Human(name) => name + " needs to be saved" case SuperHero(name, power) => name + " will save using " + power }
actions.foreach(println)
Pattern matching and extracting just enough
43
val actions = for (character <- characters) yield character match { case Human(name) => name + " needs to be saved" case SuperHero(_, power) => "Could be saved using " + power }
actions.foreach(println)
// Programmer needs to be saved// Could be saved using money// Could be saved using testing
val actions = for (character <- characters) yield character match { case Human(name) => name + " needs to be saved" case SuperHero(_, power) => "Could be saved using " + power }
actions.foreach(println)
// Programmer needs to be saved// Could be saved using money// Could be saved using testing
Regular expressions
44
val text = "Ramnivas Laddad"
val Name = """(\w+)\s+(\w+)""".r
val person = text match { case Name(first, last) => Some(new Person(first, last)) case _ => None}
println(person) // Some(Ramnivas Laddad)
val text = "Ramnivas Laddad"
val Name = """(\w+)\s+(\w+)""".r
val person = text match { case Name(first, last) => Some(new Person(first, last)) case _ => None}
println(person) // Some(Ramnivas Laddad)
Options
45
val texts = List("Ramnivas Laddad", "foo", "Scott Andrews")
val peopleOptions = texts.map { _ match { case Name(first, last) => Some(new Person(first, last)) case _ => None }}
println(peopleOptions)// List(Some(Ramnivas Laddad), None, Some(Scott Andrews))
val texts = List("Ramnivas Laddad", "foo", "Scott Andrews")
val peopleOptions = texts.map { _ match { case Name(first, last) => Some(new Person(first, last)) case _ => None }}
println(peopleOptions)// List(Some(Ramnivas Laddad), None, Some(Scott Andrews))
Options: flattening
46
val texts = List("Ramnivas Laddad", "foo", "Scott Andrews")
val peopleOptions = texts.map { _ match { case Name(first, last) => Some(new Person(first, last)) case _ => None }}
println(peopleOptions.flatten)// List(Ramnivas Laddad, Scott Andrews)
val texts = List("Ramnivas Laddad", "foo", "Scott Andrews")
val peopleOptions = texts.map { _ match { case Name(first, last) => Some(new Person(first, last)) case _ => None }}
println(peopleOptions.flatten)// List(Ramnivas Laddad, Scott Andrews)
Options: flatMap
47
val texts = List("Ramnivas Laddad", "foo", "Scott Andrews")
val people = texts.flatMap { _ match { case Name(first, last) => Some(new Person(first, last)) case _ => None }}
println(people)// List(Ramnivas Laddad, Scott Andrews)
val texts = List("Ramnivas Laddad", "foo", "Scott Andrews")
val people = texts.flatMap { _ match { case Name(first, last) => Some(new Person(first, last)) case _ => None }}
println(people)// List(Ramnivas Laddad, Scott Andrews)
Higher order functions
def process() : Unit = { retry(5) { ... }}
def process() : Unit = { retry(5) { ... }}
def retry[T](maxRetry: Int)(thunk: => T) = { def retry(thunk: => T, attempt: Int): T = { try { thunk } catch { case ex if (attempt < maxRetry) => retry(thunk, attempt + 1) } } retry(thunk, 0)}
def retry[T](maxRetry: Int)(thunk: => T) = { def retry(thunk: => T, attempt: Int): T = { try { thunk } catch { case ex if (attempt < maxRetry) => retry(thunk, attempt + 1) } } retry(thunk, 0)}
ThunkThunk
Caching using Scala
def getQuoteGraph(stock: Stock, days: Int) : Array[Byte] = { cached("chart", stock.ticker + ":" + days) {
... Expensive calculation
}}
Caching higher-order function
abstract class Caching(val cacheManager: CacheManager) { def cached[T](region: String, key: Any) (thunk: => T): T = { val cache = ...
if (cache.containsKey(key)) { cache.get(key).asInstanceOf[T] } else { val thunkVal: T = thunk cache.put(key, thunkVal) thunkVal } }}
Transaction management
def findOrder(orderId: Long) : Order = { transactional(readOnly=true) { //... }} def updateOrder(order: Order) { transactional() { //... }}
51
Transaction management function
def transactional[T](propgation: Propagation = Propagation.REQUIRED, isolation: Isolation = Isolation.DEFAULT, readOnly: Boolean = false, timeout: Int =TransactionDefinition.TIMEOUT_DEFAULT, rollbackFor: List[Throwable] = List(), noRollbackFor: List[Throwable] = List()) (thunk: => T) : T
Transaction management implementation
abstract class TransactionManagement(val txManager: PlatformTransactionManager) {
def transactional[T](...)(thunk: => T) : T = { val txAttribute = new TransactionAttributeWithRollbackRules(...)
val status = txManager.getTransaction(txAttribute)
try { val ret = thunk txManager.commit(status) ret } catch { case ex => { if (txAttribute.rollbackOn(ex)) { txManager.rollback(status) } else { txManager.commit(status) } throw ex } } }}
There is more… a lot more
• Methods/functions– Default parameters– Named parameters– Curried parameters– Partial functions
• Type system– Higher-kinded types– Bounded types– Implicit type conversion– Type parameter evidence– Type aliasing
• Lazy values• Partial imports• Actors• Extractors• Scala ecosystem• Combinator/parser• Continuations• Compiler plugin• …
54
Learning Scala
• Read a Scala book– Get the whole picture
• May feel complex at first– Java-style Scala may serve best during initial exploration
– Over time you will appreciate its simplicity
• Learn Haskell first!– Might help to break from the Java way of thinking
55
Be ready to be humbled
Scala for Java Developers
© 2011 SpringOne 2GX. All rights reserved. Do not distribute without permission.