We all java programmers know the limitations of the switch statements and pattern matching.
In java to deal with the pattern matching based on the type of the object, we have two choices
1. Clutter the code with instance of conditions
2. Visitor pattern which needs an interface, a visitor implementation for each time and the visit method.
Scala's pattern matching is much more powerful. A lot of code written for visitor class is not required in scala.
A usual example is printing employee directory. For a manager, we have to print the number of reports and for a employee we don't. In java we would either write a if instance of or a visitor pattern t achieve this task. In most cases we would generate visitors to have open design.
All this boilerplate code can be done in a very concise fashion in scala.
The pattern matching can be used to match the type as well as the structural components of a object.
The class should be defined as a case class for structure based pattern matching.
Let us take a look at such example.
Let us define , Person and extend it to Manager.
case class Person (var id: String, var name:String, var citizen:String)
class Manager(id: String, name:String, citizen: String, val reports: Int)
extends Person(id,name, citizen)
Now let us write a class that will print reports for a Manager and validate the USA citizens by calling FBI fingerprint and ssn or Euro validations for euro citizens.
1. static methods of Java, companion object methods in scala
Scala does not allow static methods like Java does, static methods violate the oo concepts.
In scala a companion object is defines to define static methods.
In java, we would typically define such validation methods as static methods, in scala we will define them as follows.
class ValidatePersons {
}
object ValidatePersons {
def validateSsn(ssn: String): Boolean = { println("Will validate SSN"); true }
def FBIFingerPrints(ssn: String): Boolean = { println("Will validate FBI Fingerprints"); true }
def validateEuroPid(eid: String): Boolean = { println("Will validate Euro PID"); false }
}
Now let us see the power of the pattern matching in scala, it is very clear to a Java programmer how much boilerplate code is avoided in here.
def validateCredentials(person: Person): Boolean = {
var usalist = new ListBuffer[(String) => Boolean]
usalist += ValidatePersons.FBIFingerPrints
usalist += ValidatePersons.validateSsn
var eurolist = new ListBuffer[(String) => Boolean]
eurolist += ValidatePersons.validateEuroPid
person match {
case person: Manager => {
println(" Manager Record : Reports " + person.reports)
}
case Person(id, name, "USA") =>
println("Person record in USA " + person.name)
for (func <- usalist) applyValidations(func, id)
true
case Person(id, name, "EURO") =>
println("Person record in EURO " + person.name)
for (func <- eurolist) applyValidations(func, id)
true
}
false;
}
def applyValidations(f: (String) => Boolean, v: String) = f(v)
}
Points to note here are
1. Look at the declaration var usalist = new ListBuffer[(String) => Boolean]
usalist defines a list of functions, a function that takes String as input and returns a Boolean.
This is Structure Type declaration.
2. The switch statement in scala for the variable person is person match {
There is no switch, no break for the cases.
3. Now we see two flavors of match.
For a manger, the match is only for the type.case person: Manager =>
4. The next two match statements are more based on the structure of the person object, for which it is required to declare the class as Case class. If you do the javap for this class, you will see the hashcode and equals implemented.
5. The code just loops through the validation functions stored in the list (usalist or eurolist) and then applies these to the id. This is what the applyValidations does, def applyValidations(f: (String) => Boolean, v: String) = f(v)
In the next posts, I would like to explore the power of high order funtions, contra variance and co-variance in scala.
In java to deal with the pattern matching based on the type of the object, we have two choices
1. Clutter the code with instance of conditions
2. Visitor pattern which needs an interface, a visitor implementation for each time and the visit method.
Scala's pattern matching is much more powerful. A lot of code written for visitor class is not required in scala.
A usual example is printing employee directory. For a manager, we have to print the number of reports and for a employee we don't. In java we would either write a if instance of or a visitor pattern t achieve this task. In most cases we would generate visitors to have open design.
All this boilerplate code can be done in a very concise fashion in scala.
The pattern matching can be used to match the type as well as the structural components of a object.
The class should be defined as a case class for structure based pattern matching.
Let us take a look at such example.
Let us define , Person and extend it to Manager.
case class Person (var id: String, var name:String, var citizen:String)
class Manager(id: String, name:String, citizen: String, val reports: Int)
extends Person(id,name, citizen)
Now let us write a class that will print reports for a Manager and validate the USA citizens by calling FBI fingerprint and ssn or Euro validations for euro citizens.
1. static methods of Java, companion object methods in scala
Scala does not allow static methods like Java does, static methods violate the oo concepts.
In scala a companion object is defines to define static methods.
In java, we would typically define such validation methods as static methods, in scala we will define them as follows.
class ValidatePersons {
}
object ValidatePersons {
def validateSsn(ssn: String): Boolean = { println("Will validate SSN"); true }
def FBIFingerPrints(ssn: String): Boolean = { println("Will validate FBI Fingerprints"); true }
def validateEuroPid(eid: String): Boolean = { println("Will validate Euro PID"); false }
}
Now let us see the power of the pattern matching in scala, it is very clear to a Java programmer how much boilerplate code is avoided in here.
def validateCredentials(person: Person): Boolean = {
var usalist = new ListBuffer[(String) => Boolean]
usalist += ValidatePersons.FBIFingerPrints
usalist += ValidatePersons.validateSsn
var eurolist = new ListBuffer[(String) => Boolean]
eurolist += ValidatePersons.validateEuroPid
person match {
case person: Manager => {
println(" Manager Record : Reports " + person.reports)
}
case Person(id, name, "USA") =>
println("Person record in USA " + person.name)
for (func <- usalist) applyValidations(func, id)
true
case Person(id, name, "EURO") =>
println("Person record in EURO " + person.name)
for (func <- eurolist) applyValidations(func, id)
true
}
false;
}
def applyValidations(f: (String) => Boolean, v: String) = f(v)
}
Points to note here are
1. Look at the declaration var usalist = new ListBuffer[(String) => Boolean]
usalist defines a list of functions, a function that takes String as input and returns a Boolean.
This is Structure Type declaration.
2. The switch statement in scala for the variable person is person match {
There is no switch, no break for the cases.
3. Now we see two flavors of match.
For a manger, the match is only for the type.case person: Manager =>
4. The next two match statements are more based on the structure of the person object, for which it is required to declare the class as Case class. If you do the javap for this class, you will see the hashcode and equals implemented.
5. The code just loops through the validation functions stored in the list (usalist or eurolist) and then applies these to the id. This is what the applyValidations does, def applyValidations(f: (String) => Boolean, v: String) = f(v)
In the next posts, I would like to explore the power of high order funtions, contra variance and co-variance in scala.
No comments:
Post a Comment