版本:scala 2.11.8
我在继承中定义了一个具有特殊类型和覆盖方法的类:
class Father[@specialized(Int) A]{
def get(from: A): A = from
}
class Son extends Father[Int]{
override def get(from: Int): Int = {
println("Son.get")
super.get(from)
}
}
new Son().get(1) // will cause infinite recursion
那么,如何重用具有专门注释的超类方法呢?
答案 0 :(得分:3)
来自文章Quirks of Scala Specialization:
避免超级电话
合格的超级电话(可能从根本上)打破专业化。在专业化阶段正确地重新连接超级访问器方法是一个迄今为止尚未解决的噩梦。所以,至少就目前而言,像瘟疫一样避免它们。特别是,可堆叠的修改模式不适用于它。
所以它很可能是一个编译器错误,一般来说你不应该使用Scala专门化的super
调用。
经过一番调查后:
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]
的非专用通用版本,遗憾的是,它不可能{ {1}}使用专门的类调用。
现在将super
更改为特征后会发生什么(使用Scala 2.12):
Father
它看起来不是在父类中调用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
中包含值,这可能是一个错误,或者可能是对特征的专门化支持在Scala 2.12中删除了。