将Scala特性与Java中实现的方法结合使用
我想从Java调用Scala traits中实现的方法是不可能的,或者有办法吗 假设我在Scala中有:将Scala特性与Java中实现的方法结合使用,java,scala,scala-java-interop,Java,Scala,Scala Java Interop,我想从Java调用Scala traits中实现的方法是不可能的,或者有办法吗 假设我在Scala中有: trait Trait { def bar = {} } 在Java中,如果我将其用作 class Foo implements Trait { } Java抱怨Trait不是抽象的,并且没有覆盖Trait中的抽象方法bar() 从Java的角度来看,Trait.scala被编译成Trait接口。因此,在Java中实现Trait被解释为实现了一个接口,这使得错误消息显而易见。简短回答
trait Trait {
def bar = {}
}
在Java中,如果我将其用作
class Foo implements Trait {
}
Java抱怨Trait不是抽象的,并且没有覆盖Trait中的抽象方法bar()
从Java的角度来看,Trait.scala
被编译成Trait
接口。因此,在Java中实现Trait
被解释为实现了一个接口,这使得错误消息显而易见。简短回答:您不能利用Java中的trait实现,因为这将在Java中启用多重继承(!)
它是如何在Scala中实现的?
详细回答:那么它在Scala中是如何工作的呢?查看生成的字节码/类,可以找到以下代码:
interface Trait {
void bar();
}
abstract class Trait$class {
public static void bar(Trait thiz) {/*trait implementation*/}
}
class Foo implements Trait {
public void bar() {
Trait$class.bar(this); //works because `this` implements Trait
}
}
是一个接口Trait
- 抽象的
(不要与Trait$class
混淆)类是透明创建的,从技术上讲,它不实现Trait.class
接口。但是,它确实有一个Trait
方法,该方法将静态条()
实例作为参数(类似于Trait
)this
实现Foo
接口Trait
通过委托给scalac
自动实现Trait$class
方法。这本质上意味着调用Trait
Trait$class.bar(This)
Trait$class
既不是Foo
的成员,也不是Foo
的扩展。它只需通过传递this
来委托给它
多性状混合
要继续偏离Scala的工作原理。。。也就是说,很容易想象多种特征的混合在下面是如何起作用的:
trait Trait1 {def ping(){}};
trait Trait2 {def pong(){}};
class Foo extends Trait1 with Trait2
翻译为:
class Foo implements Trait1, Trait2 {
public void ping() {
Trait1$class.ping(this); //works because `this` implements Trait1
}
public void pong() {
Trait2$class.pong(this); //works because `this` implements Trait2
}
}
覆盖同一方法的多个特征
现在,很容易想象多个特征如何混合覆盖同一方法:
trait Trait {def bar(){}};
trait Trait1 extends Trait {override def bar(){}};
trait Trait2 extends Trait {override def bar(){}};
同样地,Trait1
和Trait2
将成为扩展Trait
的接口。现在,如果定义Foo
时Trait2
排在最后:
class Foo extends Trait1 with Trait2
您将获得:
class Foo implements Trait1, Trait2 {
public void bar() {
Trait2$class.bar(this); //works because `this` implements Trait2
}
}
但是,切换Trait1
和Trait2
(将Trait1
设为最后一个)将导致:
class Foo implements Trait2, Trait1 {
public void bar() {
Trait1$class.bar(this); //works because `this` implements Trait1
}
}
可堆叠修改
现在考虑可堆叠修改的特性是如何工作的。想象一下有一个非常有用的类Foo:
class Foo {
def bar = "Foo"
}
您希望通过使用traits的一些新功能来丰富这些特性:
trait Trait1 extends Foo {
abstract override def bar = super.bar + ", Trait1"
}
trait Trait2 extends Foo {
abstract override def bar = super.bar + ", Trait2"
}
以下是关于类固醇的新“Foo”:
class FooOnSteroids extends Foo with Trait1 with Trait2
它的意思是:
class Foo implements Trait1, Trait2 {
public void ping() {
Trait1$class.ping(this); //works because `this` implements Trait1
}
public void pong() {
Trait2$class.pong(this); //works because `this` implements Trait2
}
}
Trait1
Trait2
食物甾体
因此,整个堆栈调用如下所示:
- FooOnSteroids实例上的“bar”方法(入口点)李>
- Trait2$class的“bar”静态方法将此作为参数传递,并返回“Trait2$$super$bar()”方法调用和字符串“Trait2”的串联李>
- FooOnSteroids实例上的“Trait2$$super$bar()”,该实例调用
- Trait1$class的'bar'静态方法将此作为参数传递,并返回'Trait1$$super$bar()'方法调用和字符串“Trait1”的串联李>
- FooOnSteroids实例上的“Trait1$$super$bar”,该实例调用
- 原始Foo的“bar”方法
如果你已经阅读了所有内容,原始问题的答案就在前四行…这确实不是抽象的,因为
bar
返回的是一个空的单元
(一种NOP)。尝试:
然后,
bar
将是一个Java抽象方法,返回void
,这就是我的问题。如果我在Trait
的bar
中实现了一个复杂的方法,我希望在Java类中重用它,就像在Scala中一样。但我想这是不可能的。唯一的解决办法似乎是将Trait转换成一个抽象类。你应该把它变成一个抽象类。或者更好的做法是,将其作为一个特性,但添加一个抽象类,该类只是扩展了它(以具有Java绑定)。然后,您可以从Scala扩展trait,或者从Java继承抽象类。是的,尽管这将占用Java抽象类的唯一可用位置。其他解决方案是使用组合/聚合。您可以使用生成的静态方法(参见Tomasz答案)或者编写一个适合Java交互的对象。。。它缺少了抽象覆盖的工作方式,但在其他方面它非常好。:-)谢谢还有一个输入错误:当你打算切换Trait1和Trait2时,你又写了一次类Foo实现了Trait1,Trait2
。@KajMagnus:这部分是Tvaroh添加的,也许他可以修复它?@TomaszNurkiewicz抱歉我误读了答案。我认为这是正确的。(没有注意到相关的代码片段实际上是Java代码而不是Scala代码)。+1作为一名编译器开发人员(和爱好者),这些信息非常有用。现在我不需要自己进行反向工程。非常感谢这是一个错误吗?这句话不应该看起来像“Java抱怨Foo不是抽象的,并且没有覆盖Trait中的抽象方法bar()”吗?
public interface Trait2 {
String Trait2$$super$bar();
String bar();
}
public abstract class Trait2$class {
public static String bar(Trait2 thiz) {
// interface call Trait2$$super$bar() is possible
// since FooOnSteroids implements Trait2 (see below)
return thiz.Trait2$$super$bar() + ", Trait2";
}
}
class FooOnSteroids extends Foo implements Trait1, Trait2 {
public final String Trait1$$super$bar() {
// call superclass 'bar' method version
return Foo.bar();
}
public final String Trait2$$super$bar() {
return Trait1$class.bar(this);
}
public String bar() {
return Trait2$class.bar(this);
}
}
trait Trait {
def bar: Unit
}