If you want to have a reusable abstract class/trait, which needs to create instances of the subclasses, then you are probably going to use the abstract factory method pattern.
Let's have a look at the example below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
trait CurrencyLike[Repr <: CurrencyLike[Repr]]{ | |
val value : Double | |
def make(d:Double) : Repr | |
def +(x: Repr) = make(x.value + value) | |
} | |
class USD(val value: Double) extends CurrencyLike[USD] { def make(d: Double) = new USD(d) } | |
class EUR(val value: Double) extends CurrencyLike[EUR] { def make(d: Double) = new EUR(d) } | |
def plus[T <: CurrencyLike[T]](c1: T, c2:T) : T = c1 + c2 |
Make is the abstract factory method. We want it to return something of type USD when called on USD class or something of type EUR when called on instance of EUR class.
In order to tell the type checker what is the right type returned by the make method, we introduce the type parameter Repr which is restricted to be a subtype of CurrencyLike (Repr <: CurrencyLike[Repr]). The concrete class such as EUR needs to set the Repr to itself (ie. EUR), and implement the make method. That way + function which uses make to create an instance of currency returns EUR when called on EUR instance or in turn it returns USD when called on instance of USD class.
I hope it helps!
No comments:
Post a Comment