從文章Quirks of Scala Specialization:
避免超級調用
合格的超級電話是(或許根本)專業化打破。在專業化階段重新連接超級訪問器方法是迄今尚未解決的噩夢。所以,至少現在,避免它們像瘟疫一樣。特別是,可堆疊修改模式不能很好地工作。
所以這是最有可能是編譯器缺陷,一般不應使用super
呼叫使用Scala專業化。
有點調查後:
javap -c Son.class
public class Son extends Father$mcI$sp {
public int get(int);
Code:
0: aload_0
1: iload_1
2: invokevirtual #14 // Method get$mcI$sp:(I)I
5: ireturn
public int get$mcI$sp(int);
Code:
0: getstatic #23 // Field scala/Predef$.MODULE$:Lscala/Predef$;
3: ldc #25 // String Son.get
5: invokevirtual #29 // Method scala/Predef$.println:(Ljava/lang/Object;)V
8: aload_0
9: iload_1
10: invokespecial #31 // Method Father$mcI$sp.get:(I)I
13: ireturn
Son.get(int)
電話Son.get$mcI$sp(int)
它變成Father$mcI$sp.get(int)
:
javap -c Father\$mcI\$sp.class
public class Father$mcI$sp extends Father<java.lang.Object> {
public int get(int);
Code:
0: aload_0
1: iload_1
2: invokevirtual #12 // Method get$mcI$sp:(I)I
5: ireturn
public int get$mcI$sp(int);
Code:
0: iload_1
1: ireturn
看起來我們已經找到了原因 - Father$mcI$sp.get(int)
使得以get$mcI$sp
虛擬呼叫,這是在Son
超載!這是造成無限遞歸的原因。
編譯器必須創建方法get
這是get$mcI$sp
的專門版本,以支持Father[T]
非專業的普通版本,不幸的是使得它不可能有super
電話與專用類。
改變Father
是一個特質(使用Scala 2.12)後,現在發生了什麼:
javap -c Son.class
public class Son implements Father$mcI$sp {
public int get$mcI$sp(int);
Code:
0: getstatic #25 // Field scala/Predef$.MODULE$:Lscala/Predef$;
3: ldc #27 // String Son.get
5: invokevirtual #31 // Method scala/Predef$.println:(Ljava/lang/Object;)V
8: aload_0
9: iload_1
10: invokestatic #37 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
13: invokestatic #43 // InterfaceMethod Father.get$:(LFather;Ljava/lang/Object;)Ljava/lang/Object;
16: invokestatic #47 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
19: ireturn
它看起來像,而不是在父類中調用get$mcI$sp
,它調用靜態方法Father.get$
:
javap -c Father.class
public interface Father<A> {
public static java.lang.Object get$(Father, java.lang.Object);
Code:
0: aload_0
1: aload_1
2: invokespecial #17 // InterfaceMethod get:(Ljava/lang/Object;)Ljava/lang/Object;
5: areturn
public A get(A);
Code:
0: aload_1
1: areturn
public static int get$mcI$sp$(Father, int);
Code:
0: aload_0
1: iload_1
2: invokespecial #26 // InterfaceMethod get$mcI$sp:(I)I
5: ireturn
public int get$mcI$sp(int);
Code:
0: aload_0
1: iload_1
2: invokestatic #33 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
5: invokeinterface #17, 2 // InterfaceMethod get:(Ljava/lang/Object;)Ljava/lang/Object;
10: invokestatic #37 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
13: ireturn
其中有趣的是,它似乎像get
方法,因爲它沒有得到真正的專業化公頃s來打包get$mcI$sp
中的值,這可能是一個錯誤,或者Scala 2.12中可能會丟失對特徵的專業化支持。
把'Father'變成特質似乎解決了這個問題,但我不確定這個原因。 – adamwy