您的預期存在矛盾。
你是「驚訝」地看到,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