記事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 で削除された可能性があります。