Using Traits from Java

You’ll run into a few idiosyncrasies when using traits from Java—nothing impossible, but there are some rough edges we have to work around, so let’s walk through this section in smaller steps.

Scala traits with no method implementation are simple interfaces at the bytecode level. Scala doesn’t support the interface keyword. If you want to create interfaces in Scala, you’d create traits with no implementation in them. Here’s an example of a Scala trait, which in effect is simply an interface:

Intermixing/Writable.scala
 
trait​ Writable {
 
def​ write(message: ​String​) : ​Unit
 
}

The trait has one abstract method that should be implemented by any class that mixes in this trait. On the Java side, Writable is seen like any interface. Implementing that interface is straightforward:

Intermixing/AWritableJavaClass.java
 
//Java code
 
public​ ​class​ AWritableJavaClass ​implements​ Writable {
 
public​ ​void​ write(​String​ message) {
 
//...code...
 
}
 
}

When working with a trait with no implementation, think about it as a simple interface and implement it much like the way you’d implement interfaces in Java. Nothing to worry about—not yet.

If a trait has method implementations, then the Scala compiler creates two things: an interface with the abstract method declarations and, in addition, a corresponding abstract class that holds the implementations. If you merely want to implement that interface in Java, that’s no problem. However, for a trait with implementations, you’d want to make use of the implementations from Java. That’s the part that gets tricky, but only a bit.

Knowing what Scala really does is mandatory for intermixing with Java. Two things are very helpful when the going gets tough: a good dose of the caffeinated beverage of your choice and the javap tool. To understand this better, let’s try out an example with a Printable trait that has one method with an implementation:

Intermixing/Printable.scala
 
trait​ Printable {
 
def​ print() {
 
println(​"running printable..."​)
 
}
 
}

Let’s see how to implement that trait in Java and also make use of the implementation that’s in the trait.

Run the following commands to compile the trait:

 
mkdir -p classes
 
scalac -d classes Printable.scala

In the classes directory, the compiler creates two files: Printable.class and Printable$class.class (yep, that’s really the name of the file—and people think my name’s weird). Examine these two files with the following command:

 
javap classes/Printable.class classes/Printable$class.class

The javap tool gives a clear view of what the Scala compiler has been up to:

 
Compiled from "Printable.scala"
 
public abstract class Printable$class {
 
public static void print(Printable);
 
public static void $init$(Printable);
 
}
 
Compiled from "Printable.scala"
 
public abstract class Printable$class {
 
public static void print(Printable);
 
public static void $init$(Printable);
 
}

The compiler took the trait we wrote and created an abstract class with the same name as the trait—Printable. For all practical purposes this is simply an interface. Furthermore, it created an abstract class with the name of the trait suffixed with a $class. This abstract class holds the implementations from the trait, but as static methods. With this insight we can swiftly move to implement the desired Java code that makes use of implementation in the Scala trait. Let’s create a Java class that implements the trait and also makes use of the implementation in it—in essence we’re mixing the Scala trait into our Java code.

Intermixing/APrintable.java
Line 1 
public​ ​class​ APrintable ​implements​ ​Printable​ {
public​ ​void​ print() {
System​.out.println(​"We can reuse the trait here if we like..."​);
Printable​$​class​.print(this);
}
public​ ​static​ ​void​ use(​Printable​ printable) {
printable.print();
10 
}
public​ ​static​ ​void​ main(​String​​[]​ args) {
APrintable aPrintable = ​new​ APrintable();
use(aPrintable);
15 
}
}

The APrintable Java class implements the interface part of the trait Printable. Within the print method we’re providing our own implementation, but we also turn around and make use of the implementation within the trait as well. To do this, on line 4 we call the static method in the abstract class that the Scala compiler generated.

To show that we can treat an instance of the Java class as an instance of the trait, the use method receives an instance of Printable and we pass to it the instance aPrintable of the class APrintable. Within the use method we invoke the print method—this should use the implementation within the APrintable, which in turn uses the implementation within the Scala trait.

To run this code, let’s first compile the Java code and then run the java command:

 
javac -d classes -classpath $SCALA_HOME/lib/scala-library.jar:classes
 
APrintable.java
 
java -classpath $SCALA_HOME/lib/scala-library.jar:classes APrintable

The steps to compile and run are pretty simple. We only need to ensure the classpath is set properly to include the Scala library and the classes related to our traits. Let’s take a look at the output:

 
We can reuse the trait here if we like...
 
running printable...

The output shows that we successfully mixed the trait into our Java class and reused the implementation that’s in the trait as well.

If you merely want to implement Scala traits in Java, then make your traits pure, with no implementation. Since traits are really interfaces, implementing them in Java is no big deal. On the other hand, if you want to mix a Scala trait into Java classes, then reach for that caffeinated beverage and the javap tool to get a grip on the lower-level details. Once you discover the actual class names that are generated, you can then move forward to use the trait from Java.

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

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