This presentation is based on Joakim\’s experiences from moving from Java to Scala http://www.scala-lang.org/node/960#Joak We will explore how to move from Java to Scala and why. We\’ll look at things that you will run into sooner rather than later such as Scala\’s collection APIs, Options and higher order functions and special syntax. You will leave this presentation with good foundation to use Scala in practice; perhaps even in your current Java project and ideally with an appetite to learn more.
2. AGENDA
1. Object orientation (OO) 2. Functional programming (FP)
1.1 Every value is an object 2.1 Every function is a value
1.2 Mixin-based composition with traits 2.2 Anonymous functions
1.3 Self-type annotations 2.3 Higher-order functions
1.4 Structural typing 2.4 Closures
1.5 Local type inference 2.5 Currying
1.6 Static type system 2.6 Immutability
1.7 Generic classes 2.7 For comprehensions
1.8 Variance annotations 2.8 Algebraic data types
1.9 Upper and lower type bounds 2.9 Pattern matching
1.10 Inner classes and abstract types as object
members 3. Practice
1.12 Compound types
1.13 Explicitly typed self references 3.1 Square roots using Newtons method
1.14 Views, and polymorphic methods. 3.2 Finding fixed points
4. WHAT WE (REALLY) WANT
YOU TO LEARN:
•A Java to Scala conversion is easy
• Scala brings a lot of new possibilities
• You don’t have to understand all new possibilities
to start with
6. OUR ALL JAVA MAIN
Defined in Java
public static void main(String[] args) {
JobFactory factory = new JdbcJobFactory();
JobControl control = new JobControl(factory);
...
}
12. public class Job {
private String name;
private List<Step> steps;
private Map<String, Object> args;
public Map<String, Object> getArgs() {
return args;
}
public void setArgs(Map<String, Object> args) {
this.args = args;
}
public List<Step> getSteps() {
return steps;
}
public void setSteps(List<Step> steps) {
this.steps = steps;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
13. public class Job {
private String name; Make it compile
private List<Step> steps;
private Map<String, Object> args; •Public is default
public Map<String, Object> getArgs() {
•Variables are
return args; declared with var
}
public void setArgs(Map<String, Object> args) { •Types after identifier
•Generics with []
this.args = args;
}
public List<Step> getSteps() {
return steps; •Functions declared
}
public void setSteps(List<Step> steps) {
with def
this.steps = steps;
}
•= {...}
public String getName() {
return name;
•void ≈ Unit
} •initialize var
public void setName(String name) {
this.name = name;
}
}
14. class Job {
private String name; Make it compile
private List<Step> steps;
private Map<String, Object> args; •Public is default
Map<String, Object> getArgs() {
•Variables are
return args; declared with var
}
void setArgs(Map<String, Object> args) { •Types after identifier
•Generics with []
this.args = args;
}
List<Step> getSteps() {
return steps; •Functions declared
}
void setSteps(List<Step> steps) {
with def
this.steps = steps;
}
•= {...}
String getName() {
return name;
•void ≈ Unit
} •initialize var
void setName(String name) {
this.name = name;
}
}
15. class Job {
private String name; Make it compile
private List<Step> steps;
private Map<String, Object> args; •Public is default
Map<String, Object> getArgs() {
•Variables are
return args; declared with var
}
void setArgs(Map<String, Object> args) { •Types after identifier
•Generics with []
this.args = args;
}
List<Step> getSteps() {
return steps; •Functions declared
}
void setSteps(List<Step> steps) {
with def
this.steps = steps;
}
•= {...}
String getName() {
return name;
•void ≈ Unit
} •initialize var
void setName(String name) {
this.name = name;
}
}
16. class Job {
private String name; Make it compile
private List<Step> steps;
private Map<String, Object> args; •Public is default
Map<String, Object> getArgs() {
•Variables are
return args; declared with var
}
void setArgs(Map<String, Object> args) { •Types after identifier
•Generics with []
this.args = args;
}
List<Step> getSteps() {
return steps; •Functions declared
}
void setSteps(List<Step> steps) {
with def
this.steps = steps;
}
•= {...}
String getName() {
return name;
•void ≈ Unit
} •initialize var
void setName(String name) {
this.name = name;
}
}
17. class Job {
private var String name; Make it compile
private var List<Step> steps;
private var Map<String, Object> args; •Public is default
Map<String, Object> getArgs() {
•Variables are
return args; declared with var
}
void setArgs(Map<String, Object> args) { •Types after identifier
•Generics with []
this.args = args;
}
List<Step> getSteps() {
return steps; •Functions declared
}
void setSteps(List<Step> steps) {
with def
this.steps = steps;
}
•= {...}
String getName() {
return name;
•void ≈ Unit
} •initialize var
void setName(String name) {
this.name = name;
}
}
18. class Job {
private var String name; Make it compile
private var List<Step> steps;
private var Map<String, Object> args; •Public is default
Map<String, Object> getArgs() {
•Variables are
return args; declared with var
}
void setArgs(Map<String, Object> args) { •Types after identifier
•Generics with []
this.args = args;
}
List<Step> getSteps() {
return steps; •Functions declared
}
void setSteps(List<Step> steps) {
with def
this.steps = steps;
}
•= {...}
String getName() {
return name;
•void ≈ Unit
} •initialize var
void setName(String name) {
this.name = name;
}
}
19. class Job {
private var String name; Make it compile
private var List<Step> steps;
private var Map<String, Object> args; •Public is default
Map<String, Object> getArgs() {
•Variables are
return args; declared with var
}
void setArgs(Map<String, Object> args) { •Types after identifier
•Generics with []
this.args = args;
}
List<Step> getSteps() {
return steps; •Functions declared
}
void setSteps(List<Step> steps) {
with def
this.steps = steps;
}
•= {...}
String getName() {
return name;
•void ≈ Unit
} •initialize var
void setName(String name) {
this.name = name;
}
}
20. class Job {
private var name:String; Make it compile
private var steps:List<Step>;
private var args:Map<String, Object>; •Public is default
getArgs():Map<String, Object> {
•Variables are
return args; declared with var
}
setArgs(args:Map<String, Object>):void { •Types after identifier
•Generics with []
this.args = args;
}
getSteps():List<Step> {
return steps; •Functions declared
}
setSteps(steps:List<Step>):void {
with def
this.steps = steps;
}
•= {...}
getName():String {
return name;
•void ≈ Unit
} •initialize var
setName(name:String):void {
this.name = name;
}
}
21. class Job {
private var name:String; Make it compile
private var steps:List<Step>;
private var args:Map<String, Object>; •Public is default
getArgs():Map<String, Object> {
•Variables are
return args; declared with var
}
setArgs(args:Map<String, Object>):void { •Types after identifier
•Generics with []
this.args = args;
}
getSteps():List<Step> {
return steps; •Functions declared
}
setSteps(steps:List<Step>):void {
with def
this.steps = steps;
}
•= {...}
getName():String {
return name;
•void ≈ Unit
} •initialize var
setName(name:String):void {
this.name = name;
}
}
22. class Job {
private var name:String; Make it compile
private var steps:List<Step>;
private var args:Map<String, Object>; •Public is default
getArgs():Map<String, Object> {
•Variables are
return args; declared with var
}
setArgs(args:Map<String, Object>):void { •Types after identifier
•Generics with []
this.args = args;
}
getSteps():List<Step> {
return steps; •Functions declared
}
setSteps(steps:List<Step>):void {
with def
this.steps = steps;
}
•= {...}
getName():String {
return name;
•void ≈ Unit
} •initialize var
setName(name:String):void {
this.name = name;
}
}
23. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
getArgs():Map[String, Object] {
•Variables are
return args; declared with var
}
setArgs(args:Map[String, Object]):void { •Types after identifier
•Generics with []
this.args = args;
}
getSteps():List[Step] {
return steps; •Functions declared
}
setSteps(steps:List[Step]):void {
with def
this.steps = steps;
}
•= {...}
getName():String {
return name;
•void ≈ Unit
} •initialize var
setName(name:String):void {
this.name = name;
}
}
24. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
getArgs():Map[String, Object] {
•Variables are
return args; declared with var
}
setArgs(args:Map[String, Object]):void { •Types after identifier
•Generics with []
this.args = args;
}
getSteps():List[Step] {
return steps; •Functions declared
}
setSteps(steps:List[Step]):void {
with def
this.steps = steps;
}
•= {...}
getName():String {
return name;
•void ≈ Unit
} •initialize var
setName(name:String):void {
this.name = name;
}
}
25. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
getArgs():Map[String, Object] {
•Variables are
return args; declared with var
}
setArgs(args:Map[String, Object]):void { •Types after identifier
•Generics with []
this.args = args;
}
getSteps():List[Step] {
return steps; •Functions declared
}
setSteps(steps:List[Step]):void {
with def
this.steps = steps;
}
•= {...}
getName():String {
return name;
•void ≈ Unit
} •initialize var
setName(name:String):void {
this.name = name;
}
}
26. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):void { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):void {
with def
this.steps = steps;
}
•= {...}
def getName():String {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):void {
this.name = name;
}
}
27. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):void { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):void {
with def
this.steps = steps;
}
•= {...}
def getName():String {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):void {
this.name = name;
}
}
28. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):void { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):void {
with def
this.steps = steps;
}
•= {...}
def getName():String {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):void {
this.name = name;
}
}
29. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):void { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):void {
with def
this.steps = steps;
}
•= {...}
def getName():String {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):void {
this.name = name;
}
}
30. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):void = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):void = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):void = {
this.name = name;
}
}
31. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):void = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):void = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):void = {
this.name = name;
}
}
32. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):void = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):void = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):void = {
this.name = name;
}
}
33. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):void = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):void = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):void = {
this.name = name;
}
}
34. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):Unit = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):Unit = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):Unit = {
this.name = name;
}
}
35. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):Unit = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):Unit = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):Unit = {
this.name = name;
}
}
36. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):Unit = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):Unit = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):Unit = {
this.name = name;
}
}
37. class Job {
private var name:String; Make it compile
private var steps:List[Step];
private var args:Map[String, Object]; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):Unit = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):Unit = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):Unit = {
this.name = name;
}
}
38. class Job {
private var name:String = null; Make it compile
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):Unit = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):Unit = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):Unit = {
this.name = name;
}
}
39. class Job {
private var name:String = null; Make it compile
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):Unit = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):Unit = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):Unit = {
this.name = name;
}
}
40. class Job {
private var name:String = null; Make it compile
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):Unit = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):Unit = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):Unit = {
this.name = name;
}
}
41. class Job {
private var name:String = null; Make it compile
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •Public is default
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
def setArgs(args:Map[String, Object]):Unit = { •Types after identifier
•Generics with []
this.args = args;
}
def getSteps():List[Step] = {
return steps; •Functions declared
}
def setSteps(steps:List[Step]):Unit = {
with def
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):Unit = {
this.name = name;
}
}
42. class Job {
private var name:String = null; Make it compile
private var steps:List[Step] = null;
•Public is default
private var args:Map[String, Object] = null;
def getArgs():Map[String, Object] = {
•Variables are
return args; declared with var
}
•Types after identifier
def setArgs(args:Map[String, Object]):Unit = {
•Generics with []
this.args = args;
}
IT COMPILES
def getSteps():List[Step] = {
return steps; •Functions declared
} BUT IT’S JAVA WITH SCALA SYNTAX
with def
def setSteps(steps:List[Step]):Unit = {
this.steps = steps;
}
•= {...}
def getName():String = {
return name;
•void ≈ Unit
} •initialize var
def setName(name:String):Unit = {
this.name = name;
}
}
43. class Job {
private var name:String = null; Remove redundancy
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •The last statement is
def getArgs():Map[String, Object] = {
returned
return args;
}
•Types are inferred
def setArgs(args:Map[String, Object]):Unit = { •No = implies Unit
•Single statement
this.args = args;
}
def getSteps():List[Step] = {
return steps; doesn’t need a block
}
def setSteps(steps:List[Step]):Unit = {
•New line implies ;
this.steps = steps;
}
def getName():String = {
return name;
}
def setName(name:String):Unit = {
this.name = name;
}
}
44. class Job {
private var name:String = null; Remove redundancy
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •The last statement is
def getArgs():Map[String, Object] = {
returned
return args;
}
•Types are inferred
def setArgs(args:Map[String, Object]):Unit = { •No = implies Unit
•Single statement
this.args = args;
}
def getSteps():List[Step] = {
return steps; doesn’t need a block
}
def setSteps(steps:List[Step]):Unit = {
•New line implies ;
this.steps = steps;
}
def getName():String = {
return name;
}
def setName(name:String):Unit = {
this.name = name;
}
}
45. class Job {
private var name:String = null; Remove redundancy
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •The last statement is
def getArgs():Map[String, Object] = {
returned
return args;
}
•Types are inferred
def setArgs(args:Map[String, Object]):Unit = { •No = implies Unit
•Single statement
this.args = args;
}
def getSteps():List[Step] = {
return steps; doesn’t need a block
}
def setSteps(steps:List[Step]):Unit = {
•New line implies ;
this.steps = steps;
}
def getName():String = {
return name;
}
def setName(name:String):Unit = {
this.name = name;
}
}
46. class Job {
private var name:String = null; Remove redundancy
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •The last statement is
def getArgs():Map[String, Object] = {
returned
args;
}
•Types are inferred
def setArgs(args:Map[String, Object]):Unit = { •No = implies Unit
•Single statement
this.args = args;
}
def getSteps():List[Step] = {
steps; doesn’t need a block
}
def setSteps(steps:List[Step]):Unit = {
•New line implies ;
this.steps = steps;
}
def getName():String = {
name;
}
def setName(name:String):Unit = {
this.name = name;
}
}
47. class Job {
private var name:String = null; Remove redundancy
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •The last statement is
def getArgs():Map[String, Object] = {
returned
args;
}
•Types are inferred
def setArgs(args:Map[String, Object]):Unit = { •No = implies Unit
•Single statement
this.args = args;
}
def getSteps():List[Step] = {
steps; doesn’t need a block
}
def setSteps(steps:List[Step]):Unit = {
•New line implies ;
this.steps = steps;
}
def getName():String = {
name;
}
def setName(name:String):Unit = {
this.name = name;
}
}
48. class Job {
private var name:String = null; Remove redundancy
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •The last statement is
def getArgs() = {
returned
args;
}
•Types are inferred
def setArgs(args:Map[String, Object]):Unit = { •No = implies Unit
•Single statement
this.args = args;
}
def getSteps() = {
steps; doesn’t need a block
}
def setSteps(steps:List[Step]):Unit = {
•New line implies ;
this.steps = steps;
}
def getName() = {
name;
}
def setName(name:String):Unit = {
this.name = name;
}
}
49. class Job {
private var name:String = null; Remove redundancy
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •The last statement is
def getArgs() = {
returned
args;
}
•Types are inferred
def setArgs(args:Map[String, Object]):Unit = { •No = implies Unit
•Single statement
this.args = args;
}
def getSteps() = {
steps; doesn’t need a block
}
def setSteps(steps:List[Step]):Unit = {
•New line implies ;
this.steps = steps;
}
def getName() = {
name;
}
def setName(name:String):Unit = {
this.name = name;
}
}
50. class Job {
private var name:String = null; Remove redundancy
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •The last statement is
def getArgs() = {
returned
args;
}
•Types are inferred
def setArgs(args:Map[String, Object]) { •No = implies Unit
•Single statement
this.args = args;
}
def getSteps() = {
steps; doesn’t need a block
}
def setSteps(steps:List[Step]) {
•New line implies ;
this.steps = steps;
}
def getName() = {
name;
}
def setName(name:String) {
this.name = name;
}
}
51. class Job {
private var name:String = null; Remove redundancy
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •The last statement is
def getArgs() = {
returned
args;
}
•Types are inferred
def setArgs(args:Map[String, Object]) { •No = implies Unit
•Single statement
this.args = args;
}
def getSteps() = {
steps; doesn’t need a block
}
def setSteps(steps:List[Step]) {
•New line implies ;
this.steps = steps;
}
def getName() = {
name;
}
def setName(name:String) {
this.name = name;
}
}
52. class Job {
private var name:String = null; Remove redundancy
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •The last statement is
def getArgs() = args;
returned
•Types are inferred
def setArgs(args:Map[String, Object]) { •No = implies Unit
•Single statement
this.args = args;
}
def getSteps() = steps;
doesn’t need a block
def setSteps(steps:List[Step]) {
•New line implies ;
this.steps = steps;
}
def getName() = name;
def setName(name:String) {
this.name = name;
}
}
53. class Job {
private var name:String = null; Remove redundancy
private var steps:List[Step] = null;
private var args:Map[String, Object] = null; •The last statement is
def getArgs() = args;
returned
•Types are inferred
def setArgs(args:Map[String, Object]) { •No = implies Unit
•Single statement
this.args = args;
}
def getSteps() = steps;
doesn’t need a block
def setSteps(steps:List[Step]) {
•New line implies ;
this.steps = steps;
}
def getName() = name;
def setName(name:String) {
this.name = name;
}
}
54. class Job {
private var name:String = null Remove redundancy
private var steps:List[Step] = null
private var args:Map[String, Object] = null •The last statement is
def getArgs() = args
returned
•Types are inferred
def setArgs(args:Map[String, Object]) { •No = implies Unit
•Single statement
this.args = args
}
def getSteps() = steps
doesn’t need a block
def setSteps(steps:List[Step]) {
•New line implies ;
this.steps = steps
}
def getName() = name
def setName(name:String) {
this.name = name
}
}
55. class Job {
private var name:String = null Remove redundancy
private var steps:List[Step] = null
private var args:Map[String, Object] = null •The last statement is
def getArgs() = args
returned
•Types are inferred
def setArgs(args:Map[String, Object]) { •No = implies Unit
•Single statement
this.args = args
}
def getSteps() = steps
doesn’t need a block
def setSteps(steps:List[Step]) {
•New line implies ;
this.steps = steps
}
def getName() = name
def setName(name:String) {
this.name = name
}
}
56. class Job {
private var name:String = null Redundancy
private var steps:List[Step] = null
private var args:Map[String, Object] = null •The last statement is
def getArgs() = args
returned
•Types are inferred
def setArgs(args:Map[String, Object]) { •No = implies Unit
•Single statement
this.args = args
}
def getSteps() = steps
doesn’t need a block
def setSteps(steps:List[Step]) {
•New line implies ;
this.steps = steps
}
def getName() = name
def setName(name:String) {
this.name = name
}
}
57. class Job {
private var name:String = null
private var steps:List[Step] = null
private var args:Map[String, Object] = null
def getArgs() = args
def setArgs(args:Map[String, Object]) {
this.args = args
}
def getSteps() = steps There is an even
shorter form!
def setSteps(steps:List[Step]) {
this.steps = steps
}
def getName() = name
def setName(name:String) {
this.name = name
}
}
58. class Job { import scala.reflect._
private var name:String = null class Job {
private var steps:List[Step] = null @BeanProperty
private var args:Map[String, Object] = null var name:String = null
@BeanProperty
def getArgs() = args var steps:List[Step] = null
@BeanProperty
var args:Map[String, Object] = null
def setArgs(args:Map[String, Object]) { }
this.args = args
}
def getSteps() = steps
def setSteps(steps:List[Step]) {
this.steps = steps
}
def getName() = name
def setName(name:String) {
this.name = name
}
}
59. public class Job { import scala.reflect._
private String name; class Job {
private List<Step> steps; @BeanProperty
private Map<String, Object> args; var name:String = null
@BeanProperty
public Map<String, Object> getArgs() { var steps:List[Step] = null
return args; @BeanProperty
} var args:Map[String, Object] = null
public void setArgs(Map<String, Object> }
args) {
this.args = args;
}
public List<Step> getSteps() {
return steps;
}
public void setSteps(List<Step> steps) {
this.steps = steps;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
60. public class Job { import scala.reflect._
private String name; class Job {
private List<Step> steps; @BeanProperty
private Map<String, Object> args; var name:String = null
@BeanProperty
public Map<String, Object> getArgs() { var steps:List[Step] = null
return args; @BeanProperty
} var args:Map[String, Object] = null
public void setArgs(Map<String, Object> }
args) {
this.args = args;
SCALA SMALL
}
public List<Step> getSteps() {
return steps;
}
public void setSteps(List<Step> steps) {
this.steps = steps;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
61. public class Job { import scala.reflect._
private String name; class Job {
private List<Step> steps; @BeanProperty
private Map<String, Object> args; var name:String = null
@BeanProperty
public Map<String, Object> getArgs() { var steps:List[Step] = null
return args; @BeanProperty
} var args:Map[String, Object] = null
public void setArgs(Map<String, Object> }
More compact
args) {
this.args = args;
}
public List<Step> getSteps() {
return steps;
}
But still a Java mindset
public void setSteps(List<Step> steps) {
this.steps = steps;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
62. import scala.reflect._
class Job { [Make it immutable]
@BeanProperty
var name:String = null •Add a constructor
@BeanProperty
var steps:List[Step] = null
•Make fields immutable
@BeanProperty
var args:Map[String, Object] = null
•Use default
arguments
•Create by name
}
63. import scala.reflect._
class Job { [Make it immutable]
@BeanProperty
var name:String = null •Add a constructor
@BeanProperty
var steps:List[Step] = null
•Make fields immutable
@BeanProperty
var args:Map[String, Object] = null
•Use default
arguments
•Create by name
}
64. import scala.reflect._
class Job ( [Make it immutable]
@BeanProperty
var name:String, •Add a constructor
@BeanProperty
var steps:List[Step],
•Make fields immutable
@BeanProperty
var args:Map[String, Object]
•Use default
arguments
•Create by name
)
{
}
65. import scala.reflect._
class Job ( [Make it immutable]
@BeanProperty
var name:String, •Add a constructor
@BeanProperty
var steps:List[Step],
•Make fields immutable
@BeanProperty
var args:Map[String, Object]
•Use default
arguments
•Create by name
)
{
}
66. import scala.reflect._
class Job ( [Make it immutable]
@BeanProperty
val name:String, •Add a constructor
@BeanProperty
val steps:List[Step],
•Make fields immutable
@BeanProperty
val args:Map[String, Object]
•Use default
arguments
•Create by name
)
{
}
67. import scala.reflect._
class Job ( [Make it immutable]
@BeanProperty
val name:String, •Add a constructor
@BeanProperty
val steps:List[Step],
•Make fields immutable
@BeanProperty
val args:Map[String, Object]
•Use default
arguments
•Create by name
)
{
}
68. import scala.reflect._
class Job ( [Make it immutable]
@BeanProperty
val name:String = “no name”, •Add a constructor
@BeanProperty
val steps:List[Step] = emptyList,
•Make fields immutable
@BeanProperty
val args:Map[String, Object] =
•Use default
emptyMap arguments
•Create by name
)
{
}
69. import scala.reflect._
class Job ( [Make it immutable]
@BeanProperty
val name:String = “no name”, •Add a constructor
@BeanProperty
val steps:List[Step] = emptyList,
•Make fields immutable
@BeanProperty
val args:Map[String, Object] =
•Use default
emptyMap arguments
•Create by name
)
{
}
70. import scala.reflect._
class Job ( [Make it immutable]
@BeanProperty
val name:String = “no name”, •Add a constructor
@BeanProperty
val steps:List[Step] = emptyList,
•Make fields immutable
@BeanProperty
val args:Map[String, Object] =
•Use default
emptyMap arguments
•Create by name
)
{
}
new Job (
name = “an empty job”
)
71. import scala.reflect._
class Job ( [Make it immutable]
@BeanProperty
val name:String = “no name”, •Add a constructor
@BeanProperty
val steps:List[Step] = emptyList,
•Make fields immutable
@BeanProperty
val args:Map[String, Object] =
•Use default
emptyMap arguments
•Create by name
)
{
}
new Job (
name = “an empty job”
)
75. 1.1
Inn
er
cla
sse
s
1.2
str
co ictfp
lle
cti key
on wo
lib rd,
rar re
1.4
ass y fle
er cti
tk on
ey
wo ,
En rd
1.5 an ums
no , g
tat en
ion eri
s, s cs,
tat
1.7
Ho ic
im
JAVA HAS EVOLVED
or
ay po
!P r ts
ro
jec
tC
oin
!
76. 1.1
Inn
er
cla
sse
s
1.2
str
co ictfp
lle
cti key
on wo
lib rd,
rar re
1.4
ass y fle
er cti
tk on
ey
wo ,
En rd
1.5 an ums
no , g
tat en
ion eri
s, s cs,
tat
1.7
Ho ic
im
JAVA HAS EVOLVED
or
ay po
!P
Good concepts have to be twisted to fit into Java
ro r ts
jec
tC
oin
!
86. Exploit Scala
public class JobControl {
private JobFactory jf; Move a “logic” class
public JobControl(JobFactory jf) { from Java to Scala so
this.jf = jf;
}
that we can use Scala
for more than just
public void startJob(String name) {
Job job = jf.create(name); beans
if (job != null) {
Map<String, Object> context =
new HashMap<String, Object>();
for (Step step : job.getSteps()) {
step.getCommand().run(context);
}
}
}
}
87. Exploit Scala
class JobControl(jf:JobFactory) { Same steps as before
def startJob(name:String) = {
val job = jf.create(name)
if (job != null) {
val context =
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
}
88. Exploit Scala
class JobControl(jf:JobFactory) { Same steps as before
Observe the for syntax
def startJob(name:String) = {
val job = jf.create(name)
if (job != null) {
val context =
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
}
89. Exploit Scala
class JobControl(jf:JobFactory) { Same steps as before
Observe the for syntax
def startJob(name:String) = {
Does not compile,
val job = jf.create(name) needs the result of
if (job != null) {
val context = getSteps to implement
new HashMap[String, Object]() a foreach-method
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
}
90. Exploit Scala
class JobControl(jf:JobFactory) { Same steps as before
Observe the for syntax
def startJob(name:String) = {
Does not compile,
val job = jf.create(name) needs the result of
if (job != null) {
val context = getSteps to implement
new HashMap[String, Object]() a foreach-method
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
} Tip: Google for
}
}
“structural typing”
91. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { Scala 2.8 to the
rescue:
def startJob(name:String) = {
val job = jf.create(name)
if (job != null) {
val context =
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
}
92. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { Scala 2.8 to the
rescue:
Implicit java
conversions!
def startJob(name:String) = {
val job = jf.create(name)
if (job != null) {
val context =
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
}
93. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { Scala 2.8 to the
rescue:
Implicit java
conversions!
def startJob(name:String) = {
val job = jf.create(name)
if (job != null) {
val context =
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
}
94. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { Scala 2.8 to the
rescue:
Implicit java
conversions!
def startJob(name:String) = {
SCALA ☠ NULL
val job = jf.create(name)
if (job != null) {
val context =
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
}
95. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { Scala 2.8 to the
rescue:
Implicit java
conversions!
def startJob(name:String) = {
SCALA
val job = jf.create(name)
if (job != null) {
val context =
OPTIONS
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
}
96. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { Scala 2.8 to the
rescue:
Implicit java
conversions!
def startJob(name:String) = {
val job = jf.create(name)
if (job != null) {
val context =
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
}
98. trait JobFactory {
def create(name:String):Job Exploit Scala
def apply(name:String):Option[Job] Create an alternative
method that optionally
returns a Job
As we will see, the
name “apply” has a
}
special meaning in
Scala
99. trait JobFactory {
def create(name:String):Job Exploit Scala
def apply(name:String):Option[Job] = { Traits can have
val job = create(name)
if (job == null) { implementations
None
}
else { Mix ins: “Compiler copy
Some(job)
} + paste :)”
}
}
100. trait JobFactory {
def create(name:String):Job Exploit Scala
def apply(name:String):Option[Job] = { Traits can have
val job = create(name)
if (job == null) { implementations
None
}
else { Mix ins: “Compiler copy
Some(job)
} + paste :)”
}
}
Everything has a
value in Scala!
101. trait JobFactory {
def create(name:String):Job Exploit Scala
def apply(name:String):Option[Job] = { Java will only see the
val job = create(name)
if (job == null) { interface so we need
}
None
an adapter.
else {
Some(job)
} An abstract base-class?
}
}
abstract class AbstractJobFactory
extends Object with JobFactory
102. trait JobFactory {
def create(name:String):Job Exploit Scala
def apply(name:String):Option[Job] = { Pattern-matching is
val job = create(name)
job match { more compact, more
case null => None
“scala” and can give
you more compiler
case job => Some(job)
} support.
}
}
NOT THE SAME AS
abstract class AbstractJobFactory
extends Object with JobFactory
SWITCH CASE IN
JAVA!!!
103. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { Time to use our
def startJob(name:String) = {
val job = jf.create(name) “improved” factory
if (job != null) {
val context =
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
}
104. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { The option “enforces”
def startJob(name:String) = {
val job = jf.apply(name). handling the None-
getOrElse(EMPTY_JOB)
case. You can’t get the
value unless you
val context =
new HashMap[String, Object]() provide what to return
for (step <- job.getSteps()) { if the option is empty
step.getCommand().run(context)
} (None)
}
}
105. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { The option “enforces”
def startJob(name:String) = {
val job = jf.apply(name). handling the None-
getOrElse(EMPTY_JOB)
case. You can’t get the
value unless you
val context =
new HashMap[String, Object]() provide what to return
for (step <- job.getSteps()) { if the option is empty
step.getCommand().run(context)
} (None)
}
}
106. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { Told you the apply
def startJob(name:String) = {
val job = jf(name). name was special!
getOrElse(EMPTY_JOB)
val context =
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
107. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { We can also execute
def startJob(name:String) = {
val job = jf(name). code if the option is
getOrElse(
{println(“hello”)
empty
error("No job with name: "+name)})
val context =
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
108. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { We can also execute
def startJob(name:String) = {
val job = jf(name). code if the option is
getOrElse(
{println(“hello”)
empty
error("No job with name: "+name)})
val context =
new HashMap[String, Object]()
SCALA
for (step <- job.getSteps()) {
FUNCTIONS
step.getCommand().run(context)
}
}
}
119. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { We can also execute
def startJob(name:String) = {
val job = jf(name). code if the option is
getOrElse(
{println(“hello”)
empty
error("No job with name: "+name)})
val context =
new HashMap[String, Object]()
SCALA
for (step <- job.getSteps()) {
FUNCTIONS
step.getCommand().run(context)
}
}
}
120. import scala.collection.JavaConversions._
Exploit Scala
class JobControl(jf:JobFactory) { We can also execute
def startJob(name:String) = {
val job = jf(name). code if the option is
getOrElse(
{println(“hello”)
empty
error("No job with name: "+name)})
val context =
new HashMap[String, Object]()
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
121. trait JobFactory {
Use functions
Functions as Factories
def create(name:String):Job
def apply(name:String):Option[Job] = {
Factories as Functions
create(name) match {
case null => None
case job => Some(job)
}
}
}
abstract class AbstractJobFactory
extends Object with JobFactory
122. trait JobFactory
extends Function[String, Option[Job]] {
Use functions
def create(name:String):Job
def apply(name:String):Option[Job] = {
Functions are objects
create(name) match { and can be extended
case null => None
case job => Some(job)
} We already implement
}
}
apply
abstract class AbstractJobFactory
extends Object with JobFactory
123. import scala.collection.JavaConversions._
Use functions
class JobControl(jf:JobFactory) {
def startJob(name:String) = {
val job = jf(name). We can now change
val context =
getOrElse(EMPTY_JOB)
our dependencies from
new HashMap[String, Object]() JobControl...
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
124. import scala.collection.JavaConversions._
Use functions
class JobControl(jf:JobFactory) {
def startJob(name:String) = {
val job = jf(name). We can now change
val context =
getOrElse(EMPTY_JOB)
our dependencies from
new HashMap[String, Object]() JobControl...
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
125. import scala.collection.JavaConversions._
Use functions
class JobControl(jf:(String)=>Option[Job]) {
def startJob(name:String) = {
val job = jf(name). Let’s depend on a
val context =
getOrElse(EMPTY_JOB)
function instead of a
new HashMap[String, Object]() factory
for (step <- job.getSteps()) {
step.getCommand().run(context)
}
}
}
126. FUNCTIONS AS FACTORIES
def emptyJobFactory(name:String) = {
Some(new Job(name, emptyJavaList, emptyJavaMap))
}
new JobControl(emptyJobFactory)
129. STILL A FACTORY IN JAVA!
Defined in Java
public static void main(String[] args) { Defined in Scala
JobFactory factory = new JdbcJobFactory();
JobControl control = new JobControl(factory);
...
}
130. STILL A FACTORY IN JAVA!
public static void main(String[] args) {
JobFactory factory = new JdbcJobFactory();
SCALA JAVA
JobControl control = new JobControl(factory);
...
}
131. STILL A FACTORY IN JAVA!
public static void main(String[] args) {
JobFactory factory = new JdbcJobFactory();
JobControl control = new JobControl(factory);
...
}
132. IN CONCLUSION
You don’t have to use everything Scala has
to offer right away!
You don’t even have to understand ALL the
concepts of Scala to start coding Scala
135. YOU HAVE BEEN WATCHING
Enno Runne
email: enno@runne.net
hlr ogge gical.com
im O ogge@a
Joak .ohlr
joa kim ogge press.com
ohlr ord
: @j
itter lrogge.w
tw
http://a ttp:/ /joh
gical.co h
http://a m/reso
gical.co urces/a
http://a m/reso r ticles/
gical.co urces/a s4jp.pd
m/reso r ticles/ f
urces/a s4jp.m
r ticles/ ov
s4jp.ke
y