Scala @specialized注释无限递归?

时间:2016-12-26 09:20:50

标签: java scala annotations specialized-annotation

版本: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

那么,如何重用具有专门注释的超类方法呢?

1 个答案:

答案 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中删除了。