从文章Scala 专业化的怪癖:
避免超级电话
合格的超级电话(可能从根本上)因专业化而被打破。在专业化阶段正确地重新布线超级访问器方法是迄今为止尚未解决的噩梦。所以,像瘟疫一样避开它们,至少现在是这样。特别是,可堆叠修改模式不能很好地使用它。
所以它很可能是一个编译器错误,一般来说你不应该使用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
没有得到真正的专业化,因为它必须将值装箱get$mcI$sp
,这可能是一个错误,或者可能是 Scala 2.12 中删除了对特征的专业化支持。