Scala @specialized 注解无限递归?
Scala @specialized annotation infinite recursion?
版本:scala 2.11.8
我定义了一个class,在继承中有专门的类型和覆盖方法:
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
那么,如何复用superclass的方法,加上专门的注解?
来自文章Quirks of Scala Specialization:
Avoid super calls
Qualified super calls are (perhaps fundamentally) broken with specialization. Rewiring the super-accessor methods properly in the specialization phase is a nightmare that has not been solved so far. So, avoid them like the plague, at least for now. In particular, stackable modifications pattern will not work with it well.
所以这很可能是一个编译器错误,一般来说你不应该使用 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
次调用专门的 classes.
现在将 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
它看起来不是在父 class 中调用 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
中,这可能是一个错误,或者可能是专业化支持for traits 在 Scala 2.12 中被删除。
版本:scala 2.11.8
我定义了一个class,在继承中有专门的类型和覆盖方法:
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
那么,如何复用superclass的方法,加上专门的注解?
来自文章Quirks of Scala Specialization:
Avoid super calls
Qualified super calls are (perhaps fundamentally) broken with specialization. Rewiring the super-accessor methods properly in the specialization phase is a nightmare that has not been solved so far. So, avoid them like the plague, at least for now. In particular, stackable modifications pattern will not work with it well.
所以这很可能是一个编译器错误,一般来说你不应该使用 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
次调用专门的 classes.
现在将 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
它看起来不是在父 class 中调用 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
中,这可能是一个错误,或者可能是专业化支持for traits 在 Scala 2.12 中被删除。