2

バージョン: 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

では、特殊なアノテーションを使用してスーパークラスのメソッドを再利用するにはどうすればよいでしょうか?

4

1 に答える 1

3

記事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$spSon

コンパイラは、特殊化されていないジェネリック バージョンの をサポートするために、 でgetあるメソッドの特殊化されたバージョンを作成する必要があります。get$mcI$spFather[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 に答える