java - Scala @specialized注解无限递归?

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


那么,如何重用带有特殊注释的超类方法呢?

最佳答案

从文章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方法没有得到真正的专业化,因为它必须将get$mcI$sp中的值装箱,这可能是一个错误,或者可能是Scala 2.12中放弃了对特性的专业化支持。

本文翻译自 https://stackoverflow.com/questions/41328949/

网站遵循 CC BY-SA 4.0 协议,转载或引用请注明出处。

标签 java scala annotations specialized-annotation


相关文章:

java - 在Android中获取地图地址或位置地址

java - ANTLR v4的mysql语法文件中的语法错误

java - TCP套接字中的IOException和EOFException有什么区别

java - 在iPhone上编程Scala应用程序? [关闭]

javascript - 他们有JavaScript注释库吗?

java - RestEasy缺少@NoJackson批注

java - 将双(或浮点数)转换为整数时会发生什么?

java - 使用el-get安装ensime失败

html - 如何在Play中仅将一页使用特定样式表

java - @PersistenceUnit注释不会创建EntityManageFactory emf = null