I am writing an API that I want to be usable from both Scala and Java. I have a polymorphic method defined in a Scala class, and I am having trouble calling it from Java.
According to this interoperability FAQ at scala-lang.org, using a Scala class that uses advanced language features “can be tricky” but it does not say it is impossible. Since how Scala’s advanced features translate to Java is not documented, it suggests disassembling the class file to experiment with how it works.
Example Code
class Polymorphic {
def polyMethod[T](implicit om: Manifest[T]) = {
println(om.erasure.getName)
om.erasure.newInstance
}
}
object Polymorphic {
def main(args: Array[String]) {
val objOfT = (new Polymorphic).polyMethod[String]
}
}
This works fine in scala, printing “java.lang.String“
I ran javap -c on the resulting class file and got the following listing for polyMethod:
public java.lang.Object polyMethod(scala.reflect.Manifest);
Code:
0: getstatic #20; //Field scala/Predef$.MODULE$:Lscala/Predef$;
3: aload_1
4: invokeinterface #27, 1; //InterfaceMethod scala/reflect/ClassManifest.erasure:()Ljava/lang/Class;
9: invokevirtual #33; //Method java/lang/Class.getName:()Ljava/lang/String;
12: invokevirtual #37; //Method scala/Predef$.println:(Ljava/lang/Object;)V
15: aload_1
16: invokeinterface #27, 1; //InterfaceMethod scala/reflect/ClassManifest.erasure:()Ljava/lang/Class;
21: invokevirtual #41; //Method java/lang/Class.newInstance:()Ljava/lang/Object;
24: areturn
3 Major Problems:
- The method is polymorphic in its return type
- The
Manifestparameter is not implicit in Java - I can’t actually pass the type parameter.
The real project code I’m trying to do this with is here:
https://github.com/ConnorDoyle/EnMAS/blob/master/clientServerPrototype/src/org/enmas/pomdp/State.scala
My ultimate goal is a fully generic yet type safe data dictionary. It works great in Scala, but I would really like to be able to call this method from the Java API as well. My design motivation is to keep reflection, explicit casting, and null checks out of the client code.
Edit: 18 Nov 2011 In response to comment by @kassens below
Changing the definition of polyMethod to the following:
def polyMethod[T](implicit om: Manifest[T]): T = {
println(om.erasure.getName)
om.erasure.newInstance.asInstanceOf[T]
}
gives the exact same byte code when I inspect the class file with javap.
There are several problems at play:
Unified generics in Scala. The fact that
Tis unbounded in Scala (meaning it can be instantiated with primitive types), makes the Scala compiler conservatively erase it toObject. If you change your scala code toit will return T. Note the explicit cast from
newInstance, since the Scala library method does not return the correct generic type.java.lang.Classinstead.So my advice would be to add this in your Scala code:
And then call it this way from Java:
As a side note, the bytecode is always erased. If you want to see the generic information in classfiles, look for the Signature attribute using, for instance, jclasslib.