我正在编写一个我想要从Scala和Java中使用的API。我有一个在Scala类中定义的多态方法,我在用Java调用它时遇到了麻烦。
根据scala-lang.org上的这种互操作性FAQ,使用使用高级语言功能的Scala类“可能很棘手”,但它并不是说不可能。由于Scala的高级功能如何转换为Java没有记录,因此它建议反汇编类文件以试验它的工作原理。
示例代码
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]
}
}
这在scala中正常工作,打印“java.lang.String
”
我在生成的类文件上运行了javap -c
并获得了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个主要问题:
我正在尝试使用的真实项目代码在这里: https://github.com/ConnorDoyle/EnMAS/blob/master/clientServerPrototype/src/org/enmas/pomdp/State.scala
我的最终目标是完全通用但类型安全的数据字典。它在Scala中运行良好,但我真的希望能够从Java API调用此方法。我的设计动机是保持客户端代码中的反射,显式转换和空检查。
编辑:2011年11月18日 回应以下@kassens的评论
将Manifest
的定义更改为以下内容:
polyMethod
当我用def polyMethod[T](implicit om: Manifest[T]): T = {
println(om.erasure.getName)
om.erasure.newInstance.asInstanceOf[T]
}
检查类文件时,给出完全相同的字节代码。
答案 0 :(得分:4)
有几个问题在起作用:
Scala中的统一泛型。事件T
在Scala中是无限的(意味着它可以用基本类型实例化),这使得Scala编译器保守地将其擦除为Object
。如果您将scala代码更改为
def polyMethod[T <: AnyRef](implicit om: Manifest[T]) = {
println(om.erasure.getName)
om.erasure.newInstance.asInstanceOf[T]
}
它会返回T.注意newInstance
的显式转换,因为Scala库方法没有返回正确的泛型类型。
java.lang.Class
。 所以我的建议是在你的Scala代码中添加它:
def polyMethod[T <: AnyRef](cls: Class[T]): T =
polyMethod(Manifest.classType(cls))
然后从Java中调用它:
void test(Polymorphic p) {
JavaScalaCall c = p.polyMethod(this.getClass());
}
作为旁注,字节码总是被擦除。如果要查看类文件中的通用信息,请使用例如jclasslib查找“签名”属性。
答案 1 :(得分:2)
java中具有manifest参数的等价物是传递类(显式唉,当你想要的类型是泛型类型时,它不能很好地工作)。
可以从java创建清单,但最好从scala中创建。 你可以添加
def polyMethodForJava[T](clazz: Class[T])
= polyMethod(ClassManifest.From(clazz))
当然,使用它并不像scala那样舒适。
答案 2 :(得分:2)
肯定有一些Scala方法无法从Java调用。有关一个示例(不是您遇到过的示例)的说明,请参阅here。
通常,如果要创建一个可以由Java客户端“自然”使用的接口,您可能需要编写一些适配器类或方法。
答案 3 :(得分:0)
一个建议是使用Java泛型类型,Box / Unbox是您自己的数据类型。使用类型匹配来执行操作。