通过 Java 反射找到 Scala 类型的伴随对象的可靠方法是什么?
What is a reliable way to find a Scala type's companion object via Java reflection?
我正在编写 Java(不是 Scala)代码,其中我有一个 Java Class
引用,指的是 Scala 类型 T
:
trait T {}
我现在想发现 Java Class
引用,以及 object T
对应的单例实例仅使用 Java 反射,然后调用该伴随对象中的方法。
object T {
def someMethod : String = "abc";
}
我对生成的 Scala 代码进行了逆向工程以编写以下 Java 代码:
Class<?> traitClass = ...;
Class<?> companionClass = Class.forName(traitClass.getName() + "$");
Field module = companionClass.getField("MODULE$");
Object companion = module.get(companionClass);
String abc = (String) companionClass.getMethod("someMethod").invoke(companion);
这看起来不是很可靠 - 它可能会随着未来的 Scala 编译器随时更改。有没有更稳健的方法?我也愿意调用 Scala API(来自 Java)来发现伴随实例。
您编写的代码适用于顶级(包拥有的)同伴。它也适用于嵌套 class/trait-object 对的基本情况,但有些情况下简单的 name + $
方案不成立。这是一个例子:
class C {
class D
object D
def foo() {
class K
object K
}
}
这会生成 class 文件 C
、C$D
、C$D$
、C$K
和 C$K$
。
虽然编码可能会在未来的任何时候改变,但它在很长一段时间内没有改变,也不打算改变。我刚试过 2.5.0,除了方法中嵌套的 classes 之外,class 名称是相同的。
如果您可以控制 Scala classes,最简单的方法是将方法 def companion = T
添加到特征 T
.
另一种选择是使用 Scala 反射 API:
scala> import scala.reflect.runtime.{universe => ru}
scala> val m = ru.runtimeMirror(getClass.getClassLoader)
scala> val d = classOf[C$D]
d: Class[C$D] = class C$D
scala> m.runtimeClass(m.classSymbol(d).companionSymbol.asModule.moduleClass.asClass)
res16: Class[_] = class C$D$
但这在方法中嵌套的 class 上也无法按预期工作:
scala> val k = classOf[C$K]
k: Class[C$K] = class C$K
scala> m.classSymbol(k).companionSymbol
res2: reflect.runtime.universe.Symbol = <none>
请注意,在 SO 上搜索 "scala reflection companion" 也会产生几个结果(并显示一些问题)。
我正在编写 Java(不是 Scala)代码,其中我有一个 Java Class
引用,指的是 Scala 类型 T
:
trait T {}
我现在想发现 Java Class
引用,以及 object T
对应的单例实例仅使用 Java 反射,然后调用该伴随对象中的方法。
object T {
def someMethod : String = "abc";
}
我对生成的 Scala 代码进行了逆向工程以编写以下 Java 代码:
Class<?> traitClass = ...;
Class<?> companionClass = Class.forName(traitClass.getName() + "$");
Field module = companionClass.getField("MODULE$");
Object companion = module.get(companionClass);
String abc = (String) companionClass.getMethod("someMethod").invoke(companion);
这看起来不是很可靠 - 它可能会随着未来的 Scala 编译器随时更改。有没有更稳健的方法?我也愿意调用 Scala API(来自 Java)来发现伴随实例。
您编写的代码适用于顶级(包拥有的)同伴。它也适用于嵌套 class/trait-object 对的基本情况,但有些情况下简单的 name + $
方案不成立。这是一个例子:
class C {
class D
object D
def foo() {
class K
object K
}
}
这会生成 class 文件 C
、C$D
、C$D$
、C$K
和 C$K$
。
虽然编码可能会在未来的任何时候改变,但它在很长一段时间内没有改变,也不打算改变。我刚试过 2.5.0,除了方法中嵌套的 classes 之外,class 名称是相同的。
如果您可以控制 Scala classes,最简单的方法是将方法 def companion = T
添加到特征 T
.
另一种选择是使用 Scala 反射 API:
scala> import scala.reflect.runtime.{universe => ru}
scala> val m = ru.runtimeMirror(getClass.getClassLoader)
scala> val d = classOf[C$D]
d: Class[C$D] = class C$D
scala> m.runtimeClass(m.classSymbol(d).companionSymbol.asModule.moduleClass.asClass)
res16: Class[_] = class C$D$
但这在方法中嵌套的 class 上也无法按预期工作:
scala> val k = classOf[C$K]
k: Class[C$K] = class C$K
scala> m.classSymbol(k).companionSymbol
res2: reflect.runtime.universe.Symbol = <none>
请注意,在 SO 上搜索 "scala reflection companion" 也会产生几个结果(并显示一些问题)。