Scala for Java Developers
Martin Ockajak from Zürich
Software Engineer @ Archilogic
@martin_ockajak
Outline
●
Scala – Functional & object-oriented
●
Scala vs Java
●
Similarities
●
Differences
●
Integration
●
Additional features
Similarities
Basic features
●
Syntax and keywords
●
Static type checking
●
Exception handling
●
Dynamic single dispatch
Object-oriented features
●
Classes & interfaces
●
Java Interface Scala Trait
→
●
Ad-hoc polymorphism
●
Method overloading
●
Parametric polymorphism
●
Generics
●
Access & mutability modifiers
High level features
●
Anonymous functions
●
Lambda expressions
●
Higher order functions
●
Functions as arguments and values
●
Annotations
●
Run-time reflection
Platform
●
Reference implementation on JVM
●
JAR files, packages & imports
●
Stability & performance
●
Documentation generator
●
Javadoc Scaladoc
→
Tools
●
Build systems
●
SBT, Gradle, Maven
●
IDEs
●
IntellijIDEA, Eclipse, Netbeans
●
Editors
●
Atom, Sublime, Vim, Emacs
●
Shell (REPL)
●
scala, :help, print()
Differences
Basic Syntax
●
Everything is an expression
●
Almost identical expression & block syntax
●
Semicolons not needed
●
Block evaluates to its last expression
●
Type inference
●
Type annotations can be often omitted
●
Type information is deduced by the compiler
●
Automated code translation
●
http://javatoscala.com/
Method syntax
●
Java
@Override
public void side(DoubleSupplier effect) {
System.out.println("Side" + effect.getAsDouble());
}
public Double pure() {
return 2.78;
}
●
Scala
override def side(effect: () => Double): Unit = {
println("Side" + effect)
}
def pure: Double = 2.78
Class and field syntax
●
Java
public class ScalaSyntax implements Quite, Simple {
public Double mutable;
public final String something = "No changes";
public ScalaSyntax(final Double mutable) {
this.mutable = mutable;
System.out.println("New instance");
}
}
●
Scala
class ScalaSyntax(var mutable: String) extends Quite with Simple {
println("New instance")
val something = "No changes"
}
Type parameter syntax
●
Java
public <T> T generic(Iterator<T> items) {
if (items.hasNext()) {
return items.next();
} else {
return items.next();
}
}
●
Scala
def generic[T](items: Iterator[T]): T = {
if (items.hasNext) {
items.next()
} else {
items.next()
}
}
Control flow
●
No checked exceptions
●
No switch statement
●
Pattern matching
●
No operators
●
Methods with symbolic names and infix notation
// infix notation
"Concatenate" + "Strings"
// equivalent dot notation
"Concatenate".+("Strings")
def !#%(symbolic: String): String = symbolic + "Method"
No primitive types
●
Equivalent types
●
int Int
→
●
long Long
→
●
byte Byte
→
●
short Short
→
●
double Double
→
●
float Float
→
●
Optimization via value classes
●
extends AnyVal
No classic for statement
●
Behaves like enhanced for statement
●
Java
for (int index = 0; index < 7; index++);
for (char character : "text".toCharArray()) {
System.out.println(character);
}
●
Scala
for (index <- 0 until 7) ()
for (character <- "text") {
println(character)
}
No static members
●
Lazily initialized singleton object instead
●
Companion object
●
Java
public class Data {
public String variable = "";
public static final String constant = "value";
}
●
Scala
class Data {
var variable: String = ""
}
object Data {
val constant = "value"
}
Constructor constraints
●
Constructor must call another constructor first
class ConstructorConstraints(val size: Int) {
def this(size: Int, multiplier: Int) = {
this(size * multiplier)
}
def this() {
// workaround
this(Helper.generateSize(), 1)
}
}
object Helper {
def generateSize(): Int = 0
}
Integration
Calling Java code
●
Libraries in the classpath
●
Scala and Java sources in the same project
●
src/main/scala/*.scala
●
src/main/java/*.java
"test".toString
"test".toString()
val result = new java.util.ArrayList[Int]()
class SomeResource extends AutoCloseable {
override def close(): Unit = ()
}
Called by Java code
●
Declaring checked exceptions
●
Scala
class CalledByJava {
var property = "mutator method generated"
// declare checked exception
@throws(classOf[IOException])
def send(something: String): Unit = { }
}
●
Java
CalledByJava test = new CalledByJava();
test.property();
test.property_$eq("mutator method called");
// catch checked exception
try { test.send("error"); } catch (IOException e) { }
Converting collections
import java.util
import scala.collection.JavaConverters._
// Java collection
val javaMap = new util.HashMap[String, Int]()
// Java -> Scala
val scalaMap = javaMap.asScala
scalaMap.mkString(", ")
// Scala -> Java
val javaMapAgain: util.Map[String, Int] = scalaMap.asJava
Additional Features
String interpolation
●
Programmable via custom interpolators
val neat = 7
// standard library
val substitution = s"Is $neat"
val printf = f"Number is $neat%02d"
val noEscaping = raw"somenthing"
// external library
val document = json"""{ "hello": "world" }"""
Detour to Python
●
Named arguments
●
Default arguments
●
Tuples
def very(standard: String, default: String = "value"): Unit = ()
very("text")
def handy(default: String = "value", standard: String): Unit = ()
handy(standard = "text")
def firstOf(tuple: (Int, Double)): Int = tuple._1
val tuple = (1, 2.3)
firstOf(tuple)
Flexible scoping
●
Fields in traits
●
Abstract members
●
Nested function definitions
●
Multiple classes & objects in one source file
trait FlexibleScoping {
def abstractMethod(text: String): String
val abstractField: Int
def get: String = {
def compute: String = "result"
abstractMethod(compute)
}
}
Pattern matching
●
Object decomposition
●
Companion object with unapply() method
def describe(value: Any): String = value match {
case 1 => "One"
case x: Int => s"Integer $x"
case x: Double if x < 1.2 => s"Small double $x"
case _:Float | _:Double => "Floating point"
case _ => "Anything else"
}
// one = 1, two = 2.3
val (one, two) = (1, 2.3)
// first = 1, third = 3, head = 1
val Vector(first, _, third) = Vector(1, 2, 3)
// head = 1, tail = List(2, 3)
val List(head, tail @ _*) = List(1, 2, 3)
Case classes
●
Equality, creation and decomposition is provided
●
Generated equals(), toString(), apply() and unapply() methods
●
Cannot be inherited from
●
Useful for algebraic data types
// algebraic data type
sealed abstract class Tree
case class Leaf(value: Int) extends Tree
case class Node(left: Tree, right: Tree) extends Tree
// evaluates to true
Node(Leaf(1), Leaf(2)) == Node(Leaf(1), Leaf(2))
val tree = Node(Leaf(1), Node(Leaf(2), Leaf(3)))
// value = 1
val Node(Leaf(value), _) = tree
For comprehensions
●
Syntactic construct for monadic function composition
●
Generates nested invocation of map() and flatMap()
val items = Vector(1, 2, 3)
val pairs = Map("x" -> 7, "y" -> 2)
// evaluates to Vector(2, 3, 4)
for (item <- items) yield item + 1
items.map(item => item + 1)
// evaluates to Vector((1, "x"), (1, "y"), (2, "x"), (2, "y"))
for {
item <- items if item < 3
(key, value) <- pairs
} yield { (item, key, value) }
items.filter(item => item < 3).flatMap(item => pairs.map {
case (key, value) => (item, key)
})
Implicits
●
Automated passing & conversion of method arguments
●
Searches in current scope, imports, companion objects, etc.
def more(value: Int)(implicit offset: Int): Int = 2 * value + offset
implicit val offset = 1
// implicit value passed as an argument
more(7)
more(7)(offset)
implicit def convert(value: Vector[_]): Int = value.size
// an argument converted using implicit method
more(Vector(1, 2, 3))
more(convert(Vector(1, 2, 3)))(offset)
Lazy evaluation
●
Evaluation is eager by default
●
Call-by-name evaluation syntax
●
Lazy evaluation support
def callByValue(input: Int): Int = input + 1
def callByName(input: => Int): Int = {
// input is evaluated here every time
input + 1
}
def callByNeed(input: => Int): Int = {
// input is evaluated here but only once
lazy val memoized = input
memoized
}
Other interesting features
●
Partial function invocation
●
Currying
●
Partially applied functions
●
Type classes
●
Enrich functionality of existing type without extending it
●
Dynamic type checking
●
Member lookup via applyDynamic()
●
Macros
●
Compile-time reflection & code generation
Type system overview
●
Compound types
●
Types intersection in type annotation
●
Mixin class composition
●
Multiple inheritance with traits
●
Type aliases
●
Abstract type members
●
Upper & lower type bounds
●
Type variance
Type system overview
●
View & context bounds
●
Existential types
●
Typed self references
●
Structural types
●
Anonymous types
●
Path-dependent types
●
Instance bound types
Type system overview
●
Type lambdas
●
F-bounded types
●
Self-recursive types
●
Higher-order types
●
Type constructor kinds
●
Value classes
●
Type specialization
Thank you :-)

Scala for Java Developers

  • 1.
    Scala for JavaDevelopers
  • 2.
    Martin Ockajak fromZürich Software Engineer @ Archilogic @martin_ockajak
  • 3.
    Outline ● Scala – Functional& object-oriented ● Scala vs Java ● Similarities ● Differences ● Integration ● Additional features
  • 4.
  • 5.
    Basic features ● Syntax andkeywords ● Static type checking ● Exception handling ● Dynamic single dispatch
  • 6.
    Object-oriented features ● Classes &interfaces ● Java Interface Scala Trait → ● Ad-hoc polymorphism ● Method overloading ● Parametric polymorphism ● Generics ● Access & mutability modifiers
  • 7.
    High level features ● Anonymousfunctions ● Lambda expressions ● Higher order functions ● Functions as arguments and values ● Annotations ● Run-time reflection
  • 8.
    Platform ● Reference implementation onJVM ● JAR files, packages & imports ● Stability & performance ● Documentation generator ● Javadoc Scaladoc →
  • 9.
    Tools ● Build systems ● SBT, Gradle,Maven ● IDEs ● IntellijIDEA, Eclipse, Netbeans ● Editors ● Atom, Sublime, Vim, Emacs ● Shell (REPL) ● scala, :help, print()
  • 10.
  • 11.
    Basic Syntax ● Everything isan expression ● Almost identical expression & block syntax ● Semicolons not needed ● Block evaluates to its last expression ● Type inference ● Type annotations can be often omitted ● Type information is deduced by the compiler ● Automated code translation ● http://javatoscala.com/
  • 12.
    Method syntax ● Java @Override public voidside(DoubleSupplier effect) { System.out.println("Side" + effect.getAsDouble()); } public Double pure() { return 2.78; } ● Scala override def side(effect: () => Double): Unit = { println("Side" + effect) } def pure: Double = 2.78
  • 13.
    Class and fieldsyntax ● Java public class ScalaSyntax implements Quite, Simple { public Double mutable; public final String something = "No changes"; public ScalaSyntax(final Double mutable) { this.mutable = mutable; System.out.println("New instance"); } } ● Scala class ScalaSyntax(var mutable: String) extends Quite with Simple { println("New instance") val something = "No changes" }
  • 14.
    Type parameter syntax ● Java public<T> T generic(Iterator<T> items) { if (items.hasNext()) { return items.next(); } else { return items.next(); } } ● Scala def generic[T](items: Iterator[T]): T = { if (items.hasNext) { items.next() } else { items.next() } }
  • 15.
    Control flow ● No checkedexceptions ● No switch statement ● Pattern matching ● No operators ● Methods with symbolic names and infix notation // infix notation "Concatenate" + "Strings" // equivalent dot notation "Concatenate".+("Strings") def !#%(symbolic: String): String = symbolic + "Method"
  • 16.
    No primitive types ● Equivalenttypes ● int Int → ● long Long → ● byte Byte → ● short Short → ● double Double → ● float Float → ● Optimization via value classes ● extends AnyVal
  • 17.
    No classic forstatement ● Behaves like enhanced for statement ● Java for (int index = 0; index < 7; index++); for (char character : "text".toCharArray()) { System.out.println(character); } ● Scala for (index <- 0 until 7) () for (character <- "text") { println(character) }
  • 18.
    No static members ● Lazilyinitialized singleton object instead ● Companion object ● Java public class Data { public String variable = ""; public static final String constant = "value"; } ● Scala class Data { var variable: String = "" } object Data { val constant = "value" }
  • 19.
    Constructor constraints ● Constructor mustcall another constructor first class ConstructorConstraints(val size: Int) { def this(size: Int, multiplier: Int) = { this(size * multiplier) } def this() { // workaround this(Helper.generateSize(), 1) } } object Helper { def generateSize(): Int = 0 }
  • 20.
  • 21.
    Calling Java code ● Librariesin the classpath ● Scala and Java sources in the same project ● src/main/scala/*.scala ● src/main/java/*.java "test".toString "test".toString() val result = new java.util.ArrayList[Int]() class SomeResource extends AutoCloseable { override def close(): Unit = () }
  • 22.
    Called by Javacode ● Declaring checked exceptions ● Scala class CalledByJava { var property = "mutator method generated" // declare checked exception @throws(classOf[IOException]) def send(something: String): Unit = { } } ● Java CalledByJava test = new CalledByJava(); test.property(); test.property_$eq("mutator method called"); // catch checked exception try { test.send("error"); } catch (IOException e) { }
  • 23.
    Converting collections import java.util importscala.collection.JavaConverters._ // Java collection val javaMap = new util.HashMap[String, Int]() // Java -> Scala val scalaMap = javaMap.asScala scalaMap.mkString(", ") // Scala -> Java val javaMapAgain: util.Map[String, Int] = scalaMap.asJava
  • 24.
  • 25.
    String interpolation ● Programmable viacustom interpolators val neat = 7 // standard library val substitution = s"Is $neat" val printf = f"Number is $neat%02d" val noEscaping = raw"somenthing" // external library val document = json"""{ "hello": "world" }"""
  • 26.
    Detour to Python ● Namedarguments ● Default arguments ● Tuples def very(standard: String, default: String = "value"): Unit = () very("text") def handy(default: String = "value", standard: String): Unit = () handy(standard = "text") def firstOf(tuple: (Int, Double)): Int = tuple._1 val tuple = (1, 2.3) firstOf(tuple)
  • 27.
    Flexible scoping ● Fields intraits ● Abstract members ● Nested function definitions ● Multiple classes & objects in one source file trait FlexibleScoping { def abstractMethod(text: String): String val abstractField: Int def get: String = { def compute: String = "result" abstractMethod(compute) } }
  • 28.
    Pattern matching ● Object decomposition ● Companionobject with unapply() method def describe(value: Any): String = value match { case 1 => "One" case x: Int => s"Integer $x" case x: Double if x < 1.2 => s"Small double $x" case _:Float | _:Double => "Floating point" case _ => "Anything else" } // one = 1, two = 2.3 val (one, two) = (1, 2.3) // first = 1, third = 3, head = 1 val Vector(first, _, third) = Vector(1, 2, 3) // head = 1, tail = List(2, 3) val List(head, tail @ _*) = List(1, 2, 3)
  • 29.
    Case classes ● Equality, creationand decomposition is provided ● Generated equals(), toString(), apply() and unapply() methods ● Cannot be inherited from ● Useful for algebraic data types // algebraic data type sealed abstract class Tree case class Leaf(value: Int) extends Tree case class Node(left: Tree, right: Tree) extends Tree // evaluates to true Node(Leaf(1), Leaf(2)) == Node(Leaf(1), Leaf(2)) val tree = Node(Leaf(1), Node(Leaf(2), Leaf(3))) // value = 1 val Node(Leaf(value), _) = tree
  • 30.
    For comprehensions ● Syntactic constructfor monadic function composition ● Generates nested invocation of map() and flatMap() val items = Vector(1, 2, 3) val pairs = Map("x" -> 7, "y" -> 2) // evaluates to Vector(2, 3, 4) for (item <- items) yield item + 1 items.map(item => item + 1) // evaluates to Vector((1, "x"), (1, "y"), (2, "x"), (2, "y")) for { item <- items if item < 3 (key, value) <- pairs } yield { (item, key, value) } items.filter(item => item < 3).flatMap(item => pairs.map { case (key, value) => (item, key) })
  • 31.
    Implicits ● Automated passing &conversion of method arguments ● Searches in current scope, imports, companion objects, etc. def more(value: Int)(implicit offset: Int): Int = 2 * value + offset implicit val offset = 1 // implicit value passed as an argument more(7) more(7)(offset) implicit def convert(value: Vector[_]): Int = value.size // an argument converted using implicit method more(Vector(1, 2, 3)) more(convert(Vector(1, 2, 3)))(offset)
  • 32.
    Lazy evaluation ● Evaluation iseager by default ● Call-by-name evaluation syntax ● Lazy evaluation support def callByValue(input: Int): Int = input + 1 def callByName(input: => Int): Int = { // input is evaluated here every time input + 1 } def callByNeed(input: => Int): Int = { // input is evaluated here but only once lazy val memoized = input memoized }
  • 33.
    Other interesting features ● Partialfunction invocation ● Currying ● Partially applied functions ● Type classes ● Enrich functionality of existing type without extending it ● Dynamic type checking ● Member lookup via applyDynamic() ● Macros ● Compile-time reflection & code generation
  • 34.
    Type system overview ● Compoundtypes ● Types intersection in type annotation ● Mixin class composition ● Multiple inheritance with traits ● Type aliases ● Abstract type members ● Upper & lower type bounds ● Type variance
  • 35.
    Type system overview ● View& context bounds ● Existential types ● Typed self references ● Structural types ● Anonymous types ● Path-dependent types ● Instance bound types
  • 36.
    Type system overview ● Typelambdas ● F-bounded types ● Self-recursive types ● Higher-order types ● Type constructor kinds ● Value classes ● Type specialization
  • 37.