从Java调用多态Scala方法
我正在编写一个API,我希望它可以从Scala和Java中使用。我在Scala类中定义了一个多态方法,从Java调用它时遇到了问题 根据scala-lang.org上的这种互操作性,使用使用使用高级语言功能的scala类“可能很棘手”,但它并没有说这是不可能的。由于Scala的高级特性如何转换为Java没有文档记录,因此建议分解类文件以试验其工作方式 示例代码从Java调用多态Scala方法,java,scala,interop,polymorphism,Java,Scala,Interop,Polymorphism,我正在编写一个API,我希望它可以从Scala和Java中使用。我在Scala类中定义了一个多态方法,从Java调用它时遇到了问题 根据scala-lang.org上的这种互操作性,使用使用使用高级语言功能的scala类“可能很棘手”,但它并没有说这是不可能的。由于Scala的高级特性如何转换为Java没有文档记录,因此建议分解类文件以试验其工作方式 示例代码 class Polymorphic { def polyMethod[T](implicit om: Manifest[T]) =
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
三大问题:
Manifest
参数在Java中不是隐式的polyMethod
的定义更改为以下内容:
def polyMethod[T](implicit om: Manifest[T]): T = {
println(om.erasure.getName)
om.erasure.newInstance.asInstanceOf[T]
}
当我使用
javap
检查类文件时,给出了完全相同的字节码一个建议是使用Java泛型类型,并对自己的数据类型进行装箱/取消装箱。使用类型匹配来执行您的操作。在java中,拥有清单参数的等效方法是传递类(显式地,当您想要的类型是泛型类型时,它不会工作得太好)
可以从java创建清单,但最好从scala创建清单。
你可以加上
def polyMethodForJava[T](clazz: Class[T])
= polyMethod(ClassManifest.From(clazz))
当然,使用它不会像在scala中那样舒服。当然有一些scala方法不能从Java调用。有关一个示例(不是您遇到的示例)的描述,请参阅
一般来说,如果您想创建一个Java客户端“自然”使用的接口,您可能需要编写一些适配器类或方法。有几个问题:
T
在Scala中是无界的(这意味着它可以用基元类型实例化),这使得Scala编译器保守地将其擦除到对象
。如果将scala代码更改为
def polyMethod[T <: AnyRef](implicit om: Manifest[T]) = {
println(om.erasure.getName)
om.erasure.newInstance.asInstanceOf[T]
}
然后从Java中这样称呼它:
void test(Polymorphic p) {
JavaScalaCall c = p.polyMethod(this.getClass());
}
作为旁注,字节码总是被擦除的。如果您想查看类文件中的通用信息,请使用签名属性,例如,.Hmm--
@bridge
注释非常酷,我可能最终会使用它。在这一点上,让它工作的魔法胶水似乎不会像纯Scala API那么漂亮,我宁愿不污染它。谢谢,你能解释一下你的意思吗?请参考这个Java Box/Unbox页面:我了解Java中拳击的工作原理,但我不知道它在这种情况下有什么帮助。抱歉,也许我遗漏了一些东西。事实上,我考虑的另一种方法是在标准库中发现的分散的方法:接受虚拟原型对象作为参数。无论如何,调用站点代码的作者仍然有责任明确地正确地转换结果。也许这是Java语言目前存在的一个不可避免的缺陷。该方法的返回类型不是多态的。静态返回类型为Any
:@kassens:谢谢。我对你的评论进行了编辑,但我认为这并不是根本问题。只是一个评论,如果你想看很多字节码,请看JAD:它将java作为输出
void test(Polymorphic p) {
JavaScalaCall c = p.polyMethod(this.getClass());
}