The methods in scala can be parametrized with both value and types. Value parameters are enclosed within a pair of parenthesis whereas type parameters within a pair of brackets. For example;
polymorphic.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
object polymorphic { def main(args: Array[String]) { def add(a: Int, b: Int) { val c = a + b; println(c) } def addStrings(a: String, b: String) { val s = a + b println(s) } add(72, 67); addStrings("Hello", "World"); } } |
In this example the + plus operator is used for adding numbers as well as concatenating the strings. The add method performs the addition of two numbers and returns the result. The addString method use the same + operator and concatenates the two strings specified by the user. This is default operator polymorphism provided by Scala. Below image shows the output produced by the above program.
Scala Method Polymorphism
Now let’s see an example of polymorphism in method. We will write a generic method to get the list where we will pass the type, default values and number of items in the list.
PolymorphicMethod.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
object PolymorphicMethod { def getList[T](x:T, y:Int): List[T] = { if (y == 0) Nil else x :: getList(x, y - 1) } def main(args: Array[String]) { println(getList[Int](3,2)) println(getList[String]("Hi", 3)) } } |
Below image shows the output produced when above program is executed in the Scala IDE.
Scala Explicitly typed References
Sometimes we need to explicitly specify the type of value “this” in a program. Let’s see an example for this scenario.
We have a Train abstract class as below.
Train.scala
1 2 3 4 5 6 7 8 9 10 11 12 |
abstract class Train { type Link; type Compartment <: CompartmentIntf abstract class CompartmentIntf { def join(compartment: Compartment): Link } def compartments: List[Compartment] def links: List[Link] def addCompartment: Compartment } |
The abstract class Train consists of List of links and compartments. The abstract class CompartmentIntf
is defined and contains join method which takes Compartment
as parameter. The methods compartments, links and addCompartment are declared but the implementation will be defined in concrete classes.
Now we will define the abstract class MetroTrain that extends train class as;
MetroTrain.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
abstract class MetroTrain extends Train { type Link <: LinkImpl class LinkImpl(comptA: Compartment, comptB: Compartment) { def c1 = comptA def c2 = comptB } class CompartmentImpl extends CompartmentIntf { def join(compartment: Compartment): Link = { val link = newLink(this, compartment) links = link :: links link } } protected def newCompartment: Compartment protected def newLink(c1: Compartment, c2: Compartment): Link var compartments: List[Compartment] = Nil var links: List[Link] = Nil def addCompartment: Compartment = { val compartment = newCompartment compartments = compartment :: compartments compartment } } |
This class provides a partial implementation extending Train class. The implementation details are open and thus link and compartment are left abstract. We add factory methods newLink and newCompartment since it is required to create new compartment and link objects. The methods addCompartment and join are defined using factory methods.
However above class will throw compilation error in line no 11 as “type mismatch; found : CompartmentImpl.this.type (with underlying type MetroTrain1.this.CompartmentImpl) required: MetroTrain1.this.Compartment” because “this” is assigned the type CompartmentImpl, so it’s not compatible with type Compartment required by the corresponding factory method.
Let us solve this type mismatch by using explicitly typed self reference for CompartmentImpl
as below.
1 2 3 4 5 6 7 8 9 10 |
class CompartmentImpl extends CompartmentIntf { self: Compartment => def join(compartment: Compartment): Link = { val link = newLink(this, compartment) links = link :: links link } } |
According to the new definition of CompartmentImpl
, this has type Compartment. We specify the explicit typed reference so that CompartmentImpl
denotes sub type of Compartment to be instantiate.
We will now define a concrete class ConcreteMetroTrain as;
ConcreteMetroTrain.scala
1 2 3 4 5 6 7 8 9 |
class ConcreteMetroTrain extends MetroTrain { type Link = LinkImpl type Compartment = CompartmentImpl protected def newCompartment: Compartment = new CompartmentImpl protected def newLink(c1: Compartment, c2: Compartment): Link = new LinkImpl(c1, c2) } |
We can now instantiate CompartmentImpl because now we know that CompartmentImpl denotes a subtype of type Compartment. Now create a Scala object TestTrain as;
TestTrain.scala
1 2 3 4 5 6 7 8 9 10 11 12 13 |
object TestTrain { def main(args: Array[String]) { println("Linking Compartments.....") val t: Train = new ConcreteMetroTrain val c1 = t.addCompartment val c2 = t.addCompartment val c3 = t.addCompartment c1.join(c2) c2.join(c3) } } |
That’s all for polymorphic methods and explicitly typed references in scala programming, we will look into more scala features in coming posts.