How to Call Scala From Java: Inheritance & Singletons
This is the second part of the guide to integrate Scala code from Java. You can read the first part here.
This post is about extending Scala classes and traits and also accessing the Scala object’s (singletons).
Subclassing
Extending classes from Java is possible, but advanced features of Scala type system degraded.
Extending a scala class and making use of its behavior is straigtforward:
class ScalaClass {
protected def not(x: Boolean): Boolean = !x
}
public class ExtendClass extends ScalaClass {
public boolean identity(boolean x) {
return not(not(x));
}
}
Providing behavior (template method pattern, command pattern) should work the same way, although I have not tried myself.
A more interesting case would be extending a class with an abtract type alias:
abstract class AbstractScalaClass {
type T
val x: T
}
We would, for example, extend this class in Scala like so:
class ExtendAbstract extends AbstractScalaClass {
type T = Option[Int]
val x = Some(42)
}
Java itself doesn’t have type aliases. And, as far as I know, there is no way to set a type alias from Java like we did in the class above. So some functionality will inevitably be lost. AbstractScalaClass gets compiled to something like this:
public abstract class com.muhuk.csfj.part.ii.AbstractScalaClass {
public abstract java.lang.Object x();
public com.muhuk.csfj.part.ii.AbstractScalaClass();
}
Notice that x’s type is Object. This should be no surprise because T is not constrained, i.e. T <: Any. So we can extend AbstractScalaClass from java as below:
public class ExtendAbstract extends AbstractScalaClass {
public Boolean x() {
return Boolean.FALSE;
}
}
Wait a minute. How come this compiles?
public class com.muhuk.csfj.part.ii.ExtendAbstract extends com.muhuk.csfj.part.ii.AbstractScalaClass {
public com.muhuk.csfj.part.ii.ExtendAbstract();
public java.lang.Boolean x();
public java.lang.Object x();
}
It has two x methods, one returning a Boolean and one returning an Object. It seems to work, somehow:
ExtendAbstract extendAbstract = new ExtendAbstract();
assertEquals(false, extendAbstract.x());
assertTrue(extendAbstract.x() instanceof Boolean);
A closer look at the generated code reveals that invoking public Object x() calls public Boolean x() and return its result:
public class com.muhuk.csfj.part.ii.ExtendAbstract extends com.muhuk.csfj.part.ii.AbstractScalaClass {
public com.muhuk.csfj.part.ii.ExtendAbstract();
descriptor: ()V
Code:
0: aload_0
1: invokespecial #1 // Method com/muhuk/csfj/part/ii/AbstractScalaClass."<init>":()V
4: return
public java.lang.Boolean x();
descriptor: ()Ljava/lang/Boolean;
Code:
0: getstatic #2 // Field java/lang/Boolean.FALSE:Ljava/lang/Boolean;
3: areturn
public java.lang.Object x();
descriptor: ()Ljava/lang/Object;
Code:
0: aload_0
1: invokevirtual #3 // Method x:()Ljava/lang/Boolean;
4: areturn
}
It seems, being the more specific type, the Boolean version masks the Object version. I would like to know why this code is generated. But I will not investigate it further because unconstrained type aliases are not too useful and therefore not too common.
A more realistic example is constraining the type parameter:
abstract class AbstractScalaClass {
type A <: ScalaClass
def x: A
}
This predictably compiles to:
public abstract class com.muhuk.csfj.part.ii.AbstractScalaClass {
public abstract com.muhuk.csfj.part.ii.ScalaClass x();
public com.muhuk.csfj.part.ii.AbstractScalaClass();
}
And we can extend it as:
public class ExtendAbstract extends AbstractScalaClass {
public ExtendClass x() {
return new ExtendClass();
}
}
Method return types in Java are covariant. Even if the Java version is a little less well typed, it is quite close to its Scala counterpart in this case. Note that the following doesn’t compile:
public class ExtendAbstract extends AbstractScalaClass {
public <T extends ScalaClass> T x() {
return null;
}
}
Tyring to compile the code above results in the following error:
Name clash: The method x() of type ExtendAbstract has the same erasure as x() of type AbstractScalaClass but does not override it
It fails because AbstractScalaClass.x does not have a type parameter.
Extending Traits
Using traits from Java is tricky and unavoidable at the same time since traits are the go to abstraction in Scala.
So far we dealt with classes and abstract classes. Java has classes and abstract classes. Scala versions are only slightly different, due to its advanced type system. However traits were not slightly different interfaces until Java 1.8. Now Java allows method implementations within interfaces and multiple inheritance as a result of that.
Invoking implementations within traits from Java is possible. Consider the trait below:
trait ScalaTrait {
def foo(j: Int): Int
def bar(k: Int): Int = foo(k * 2) + 3
}
Since foo is the only abstract member, this Java class should compile:
public class ExtendTrait implements ScalaTrait {
public int foo(int j) {
return j * 10;
}
}
But the compiler will fail, complaining about the unimplemented bar method. Even though Scala traits compile to Java interfaces. As of Scala 2.11.8, default interface implementations are not taken advantage of. We can still reuse the default implementation from Java, it just takes a bit extra work:
public class ExtendTrait implements ScalaTrait {
public int foo(int j) {
return j * 10;
}
public int bar(int k) {
return ScalaTrait$class.bar(this, k);
}
}
Default implementation of bar ends up being a static method on abstract class ScalaTrait$class. A detailed explanation about how traits are implemented can be found in this StackOverflow question.
Singletons
Singleton instance is <ClassName>$.MODULE$
Scala provides singletons in the form of object definitions. An object with the same name of a class/trait is called companion object. This is not purely convention. Since the class/trait and the object share the same name they are closely related. Importing the name makes both the class/trait and the object available, classes can call private methods in their companion objects, etc.
We can access the companion objects of Scala collections using the companion method:
HashMap immutableMap = new scala.collection.immutable.HashMap();
Object mapCompanion = immutableMap.companion();
assertTrue(mapCompanion instanceof scala.collection.immutable.Iterable$);
This is not very useful since companion objects are primarily used to create new instances and this method requires us to hold a reference to an instance already. There is another way to access objects, companion or not. Given this object:
object Singleton {
def foo(): String = "There can be only one."
}
We can call foo() using the MODULE$ constant on Singleton$ class:
Singleton$ singletonInstance = Singleton$.MODULE$;
assertEquals("There can be only one.", singletonInstance.foo());
This is what’s inside Singleton$:
public final class com.muhuk.csfj.part.ii.Singleton$ {
public static final com.muhuk.csfj.part.ii.Singleton$ MODULE$;
public static {};
public java.lang.String foo();
}
Notice the absence of a public constructor and the static block? It works roughly like this:
private com.muhuk.csfj.part.ii.Singleton$();
static {
MODULE$ = new Singleton$();
}
This concludes the second part of How to Call Scala From Java. Let me know if it was useful to you.
If you have any questions, suggestions or corrections feel free to drop me a line.