您的预期存在矛盾。
你是“惊讶”地看到,Scala是主要版本之间的二进制兼容,这表明你期望的恰恰相反:是斯卡拉应该是二进制兼容的,甚至主要版本之间。
然而,同时你希望Scala能够使用一种编码来依赖于Scala 2.11二进制格式设计时所不具备的特性。 Java 8发布之前的两周,第一个Scala 2.11的候选版本,即不允许更改的地方。要求每个Scala用户在发布之前安装Java 8将是荒谬的。
所以,一方面,你期望完全的二进制兼容性,即根本没有改变。另一方面,您希望使用最新和最好的功能,即尽可能快地进行更改。你不能拥有两个。你必须选择。
而且,正如阿列克谢已经在他的回答中指出,这是正是改进这个样子,那需要破坏二进制兼容性。
如果您具有二进制兼容性,如果找出更好的二进制表示形式,则无法更改二进制表示形式。当目标平台可用时,您无法利用目标平台的新功能。这是非常严格的,特别是对于像Scala这样的语言来说,它推动了JVM上合理编码的边界。编译器设计人员迫使他们第一次得到“一切正常”将会非常棘手。
这里是已经改变了多年来和破碎的向后兼容性有两件事情:
- lambda表达式的编码,使用
MethodHandle
S,当他们在Java 7中,他们不能有加“第一次得到这个权利“,因为当时MethodHandle
s甚至不存在。
- (在即将推出的2.12中).Lamdas的编码,再次,以便它们与Java 8的编码相同。他们不可能“第一次得到这个权利”,因为那时lambda甚至不存在于Java中。
- (在即将推出的2.12中)。使用
default
方法在interface
s中编码性状。他们不可能“第一次得到这个权利”,因为当时在Java中还没有存在default
方法。
如果Java平台得到适当的尾部调用或至少正确的尾递归,我敢肯定,ABI会再次改变以利用这些功能。如果我们在JVM中获得值类型,那么Scala中的值类的编码可能会改变。
然而,在dotc
, the compiler for Dotty,球队正在尝试一种新的方法,以二进制兼容性:TASTy。 TASTy是键入抽象语法树的序列化格式。这个想法是保证TASTy的二进制兼容性,但不是最终的输出。 TASTy包含重新编译程序的所有必要信息,因此如果要合并由不同编译器编译的两个闭源库,这不成问题,因为您可以丢弃已编译的代码并从TASTy重新编译。
TASTy将随编译后的代码一起发货。例如。对于Scala-JVM,序列化的TASTy将在.class
文件或.jar
的元数据部分中提供,对于已编译的源文件中的注释或二进制数组中的Scala.js,编译后的.dll
的元数据部分中的Scala本机,.exe
,.so
,.dylib
等等。
再回到你对性状的具体问题:
目前,单一的特点被编码为:包含抽象的声明所有性状的方法(既抽象又
- 的
interface
混凝土)
- 静态类包含所有特征的具体方法的静态方法,采取一个额外的参数
$this
- 在继承层次的每个点的特点是混合在性状一切具体方法,合成转发方法是期待的静态类的静态方法
所以,下面的Scala代码:
trait A {
def foo(i: Int) = i + 1
def abstractBar(i: Int): Int
}
trait B {
def baz(i: Int) = i - 1
}
class C extends A with B {
override def abstractBar(i: Int) = i * i
}
会像这样编码:
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 i + 1; }
}
interface B {
int baz(int i);
}
abstract class B$class {
static void $init$(B $this) {}
static int baz(B $this, int i) { return i - 1; }
}
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 i * i; }
}
但在斯卡拉2.12瞄准的Java 8,它看起来更像是这样的:
interface A {
static void $init$(A $this) {}
static int foo$(A $this, int i) { return i + 1; }
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 i - 1; }
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 i * i; }
}
正如您所看到的,静态方法和转发器的旧设计已被保留,它们只是被折叠到界面中。特征的具体方法现在已作为static
方法移入界面本身,转发方法不是在每个类中合成,而是一次定义为default
方法,并且静态$init$
方法(代表特征体中的代码)已被也进入了界面,使得配套静态类变得不必要。
这也许可以简化这样的:
interface A {
static void $init$(A $this) {}
default int foo(int i) { return i + 1; };
int abstractBar(int i);
}
interface B {
static void $init$(B $this) {}
default int baz(int i) { return i - 1; }
}
class C implements A, B {
public C() {
A.$init$(this);
B.$init$(this);
}
@Override public int abstractBar(int i) { return i * i; }
}
我不知道为什么没有这样做。乍一看,当前的编码可能会给我们一些前向兼容性:您可以使用由新编译器编译的特性,并使用由旧编译器编译的类,这些旧类将简单地覆盖它们从接口继承的default
转发器方法相同的。除此之外,转发方法将尝试调用不再存在的A$class
和B$class
上的静态方法。
只有没有具体成员的特征才能与Java互操作。删除你的具体方法的实施,它会阻止工作。 – Samar