記事Quirks of Scala Specializationから:
避けるスーパーは
認定スーパーコールが(おそらく基本的に)専門で破壊されている呼び出します。スペシャライゼーション段階でスーパーアクセッサメソッドを適切にリワークすることはこれまでに解決されていない悪夢です。だから、今のところ、それらをペストのように避けてください。特に、スタック可能な修正パターンはうまく動作しません。
だから、それは一般的に、あなたがScalaの専門でsuper
呼び出しを使用してはならない、最も可能性の高いコンパイラのバグです。調査のビットの後
:Father$mcI$sp.get(int)
に変身
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)
:
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
にオーバーロードされています!これが無限再帰を引き起こした原因です。
コンパイラは、残念ながらそれは不可能特化したクラスでsuper
電話を持つようになりますFather[T]
の非専門のジェネリック版を、サポートするために、get$mcI$sp
ある方法get
の専門バージョンを作成する必要があります。
:
は今(Scalaの2.12付)特性であることをFather
を変更した後に何が起こるか:代わりに、親クラスでget$mcI$sp
を呼び出すのでは、それは静的メソッドFather.get$
を呼び出すよう
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
に見えます
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
メソッドが実際の特殊化を得ていないようですスクロール2.12では、バグの可能性があるget$mcI$sp
の値をボックスに入れるか、または特性の特殊化サポートを無効にしました。
「父」を特性に変えることは問題を解決するようですが、その理由はわかりません。 – adamwy