Method Late Binding in Traits

In the previous example, the method check of the Check class was concrete. Our traits extended from this class to override that method. We saw how the call to super.check() within the traits were bound to either the trait on the left or the class that mixes in. Things get a bit more complicated if the method(s) in the base class are abstract—the method binding has to be postponed until a concrete method is known. Let’s explore this further here.

Let’s write an abstract class Writer with one abstract method, writeMessage:

UsingTraits/MethodBinding.scala
 
abstract​ ​class​ Writer {
 
def​ writeMessage(message: ​String​)
 
}

Any class extending from this class is required to implement the writeMessage method. If we extend a trait from this abstract class and call the abstract method using super, Scala will demand that we declare the method as abstract override. The combination of these two keywords is rather odd but conveys a dual intent. By using the keyword override, we’re expressing our intention to provide an implementation of a known method from the base class. At the same time, we convey that the actual final “terminal” implementation for this method will be provided by the class that mixes in the trait. So, here’s an example of traits that extend the previous class:

UsingTraits/MethodBinding.scala
 
trait​ UpperCaseWriter ​extends​ Writer {
 
abstract​ ​override​ ​def​ writeMessage(message: ​String​) =
 
super​.writeMessage(message.toUpperCase)
 
}
 
 
trait​ ProfanityFilteredWriter ​extends​ Writer {
 
abstract​ ​override​ ​def​ writeMessage(message: ​String​) =
 
super​.writeMessage(message.replace(​"stupid"​, ​"s-----"​))
 
}

Scala does two things on the call to super.writeMessage() in this code. First, it performs late binding of that call. Second, it will insist that the class that mixes these traits provides an implementation of the method.

The UpperCaseWriter trait converts the given string to uppercase and passes it down the chain. The ProfanityFilteredWriter removes mildly rude words only—and only if they appeared in lowercase. This is with the intent to illustrate the ordering of the mixin.

Now, let’s make use of these traits. First, let’s write a class StringWriterDelegate that extends from the abstract class Writer and delegates writing the message to an instance of StringWriter:

UsingTraits/MethodBinding.scala
 
class​ StringWriterDelegate ​extends​ Writer {
 
val​ writer = ​new​ java.io.StringWriter
 
 
def​ writeMessage(message: ​String​) = writer.write(message)
 
override​ ​def​ toString : ​String​ = writer.toString
 
}

We could have mixed in one or more traits in the previous definition of StringWriterDelegate. Instead, let’s mix in the traits at the time of creating an instance:

UsingTraits/MethodBinding.scala
 
val​ myWriterProfanityFirst =
 
new​ StringWriterDelegate ​with​ UpperCaseWriter ​with​ ProfanityFilteredWriter
 
 
val​ myWriterProfanityLast =
 
new​ StringWriterDelegate ​with​ ProfanityFilteredWriter ​with​ UpperCaseWriter
 
 
myWriterProfanityFirst writeMessage ​"There is no sin except stupidity"
 
myWriterProfanityLast writeMessage ​"There is no sin except stupidity"
 
 
println(myWriterProfanityFirst)
 
println(myWriterProfanityLast)

Since the ProfanityFilteredWriter is the rightmost trait in the first statement, it takes effect first. However, it takes effect second in the example in the second statement. Take the time to study the code. The method execution sequence for the two instances is shown in the following figure.

images/UsingTraits/MethodChaining.png

Here’s the output from running the code:

 
THERE IS NO SIN EXCEPT S-----ITY
 
THERE IS NO SIN EXCEPT STUPIDITY

Scala nicely avoided method collision issues and chained the calls in the order of right to left—the last trait that is mixed has the highest priority to intercept method calls.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.134.99.32