为了便于讨论,让我们看一下下面的Scala示例,该示例使用多个特征同时具有抽象方法和具体方法:
trait A {
def foo(i: Int) = ???
def abstractBar(i: Int): Int
}
trait B {
def baz(i: Int) = ???
}
class C extends A with B {
override def abstractBar(i: Int) = ???
}
目前(即从Scala 2.11开始),单个特征编码为:
一个default,其中包含所有特征特性方法的抽象声明(抽象的和具体的)
一个抽象静态类,其中包含用于所有特性的所有具体方法的静态方法,并带有一个额外的参数default(在较旧的Scala中,该类不是抽象的,但对其进行实例化没有任何意义)
在特性混入的继承层次结构的每个点上,针对特性中所有具体方法的综合转发器方法,这些方法转发给静态类的静态方法
这种编码的主要优点是,没有具体成员的特征(与接口同构)实际上被编译为接口。
interface A {
int foo(int i);
int abstractBar(int i);
}
abstract class A$class {
static void $init$(A $this) {}
static int foo(A $this, int i) { return ???; }
}
interface B {
int baz(int i);
}
abstract class B$class {
static void $init$(B $this) {}
static int baz(B $this, int i) { return ???; }
}
class C implements A, B {
public C() {
A$class.$init$(this);
B$class.$init$(this);
}
@Override public int baz(int i) { return B$class.baz(this, i); }
@Override public int foo(int i) { return A$class.foo(this, i); }
@Override public int abstractBar(int i) { return ???; }
}
但是,Scala 2.12需要Java 8,因此能够在接口中使用默认方法和静态方法,结果看起来像这样:
interface A {
static void $init$(A $this) {}
static int foo$(A $this, int i) { return ???; }
default int foo(int i) { return A.foo$(this, i); };
int abstractBar(int i);
}
interface B {
static void $init$(B $this) {}
static int baz$(B $this, int i) { return ???; }
default int baz(int i) { return B.baz$(this, i); }
}
class C implements A, B {
public C() {
A.$init$(this);
B.$init$(this);
}
@Override public int abstractBar(int i) { return ???; }
}
如您所见,保留了带有静态方法和转发器的旧设计,它们只是折叠到了接口中。 特质的具体方法现在已作为default方法移入接口本身,转发器方法并非在每个类中都被综合,而是一次定义为A$class方法,并且静态B$class方法(代表特质主体中的代码)已被使用。 也移入了接口,因此不需要伴随的静态类。
可以这样简化:
interface A {
static void $init$(A $this) {}
default int foo(int i) { return ???; };
int abstractBar(int i);
}
interface B {
static void $init$(B $this) {}
default int baz(int i) { return ???; }
}
class C implements A, B {
public C() {
A.$init$(this);
B.$init$(this);
}
@Override public int abstractBar(int i) { return ???; }
}
我不确定为什么没有这样做。 乍一看,当前的编码可能会给我们带来一些前向兼容性:您可以将使用新编译器编译的特征与旧编译器编译的类一起使用,这些旧类将简单地覆盖它们从接口继承的default504转发器方法。 相同的。 除此之外,转发器方法将尝试调用不再存在的A$class和2960383251623445445506上的静态方法,以使假设的前向兼容性实际上不起作用。