在 Java 中将 Scala 特征与已实现的方法结合使用

2022-08-31 15:34:50

我想不可能从Java调用Scala特征中实现的方法,或者有没有办法?

假设我在Scala中有:

trait Trait {
  def bar = {}
}

在Java中,如果我将其用作

class Foo implements Trait {
}

Java抱怨说Trait is not abstract and does not override abstract method bar() in Trait


答案 1

从Java的角度被编译成接口。因此,在Java中实现被解释为实现接口 - 这使您的错误消息显而易见。简短的回答:你不能利用Java中的特征实现,因为这会在Java中实现多重继承(!)Trait.scalaTraitTrait

它是如何在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$classTrait.classTraitstatic bar()Traitthis)
  • Foo实现接口Trait
  • scalac通过委派 给 自动实现方法。这实质上意味着调用 .TraitTrait$classTrait$class.bar(this)

请注意,既不是 的成员,也不是 扩展它的成员。它只是通过传递来委派给它。Trait$classFooFoothis

混合多种性状

继续关于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(){}};

再次并将成为扩展的接口。现在,如果最后定义:Trait1Trait2TraitTrait2Foo

class Foo extends Trait1 with Trait2

您将获得:

class Foo implements Trait1, Trait2 {
    public void bar() {
        Trait2$class.bar(this); //works because `this` implements Trait2
    }
}

但是,切换和(最后)将导致:Trait1Trait2Trait1

class Foo implements Trait2, Trait1 {
    public void bar() {
        Trait1$class.bar(this); //works because `this` implements Trait1
    }
}

可堆叠修改

现在考虑一下作为可堆叠修改的特征是如何工作的。想象一下,有一个非常有用的类Foo:

class Foo {
  def bar = "Foo"
}

您希望使用特征通过一些新功能来丰富它:

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

它翻译为:

特质1

interface Trait1 {
  String Trait1$$super$bar();
  String bar();
}
abstract class Trait1$class {
  public static String bar(Trait1 thiz) {
    // interface call Trait1$$super$bar() is possible
    // since FooOnSteroids implements Trait1 (see below)
    return thiz.Trait1$$super$bar() + ", Trait1";
  }
}

特质2

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";
  }
}

FooOnSteroids

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);
  }      
}

因此,整个堆栈调用如下所示:

  • FooOnSteroids实例上的'bar'方法(入口点);
  • Trait2$class 的 'bar' 静态方法将此作为参数传递,并返回 'Trait2$$super$bar()' 方法调用和字符串 “, Trait2” 的串联;
  • 'Trait2$$super$bar()' 在 FooOnSteroids 实例上调用 ...
  • Trait1$class 的 'bar' 静态方法将此作为参数传递,并返回 'Trait1$$super$bar()' 方法调用和字符串 “, Trait1” 的串联;
  • 'Trait1$$super$bar' on FooOnSteroids 实例,它调用 ...
  • 原始的傅氏“酒吧”方法

结果是“Foo,Trait1,Trait2”。

结论

如果您已经设法阅读了所有内容,那么原始问题的答案就在前四行中......


答案 2

它确实不是抽象的,因为返回一个空的(一种NOP)。尝试:barUnit

trait Trait {
  def bar: Unit
}

然后将是一个返回 Java 抽象方法。barvoid


推荐