从文章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