2

版本:斯卡拉 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

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

4

1 回答 1

3

从文章Scala 专业化的怪癖

避免超级电话

合格的超级电话(可能从根本上)因专业化而被打破。在专业化阶段正确地重新布线超级访问器方法是迄今为止尚未解决的噩梦。所以,像瘟疫一样避开它们,至少现在是这样。特别是,可堆叠修改模式不能很好地使用它。

所以它很可能是一个编译器错误,一般来说你不应该使用superScala 专业化的调用。


经过一番调查:

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! 这就是导致这里无限递归的原因。

编译器必须创建方法的专用版本,getget$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 中删除了对特征的专业化支持。

于 2016-12-26T09:35:59.733 回答