2016-12-26 28 views
2

バージョン:専門的な注釈をスーパークラスのメソッドを再利用する方法、だから、Scala @specialized注釈無限再帰?

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 

:スカラ座2.11.8

私は相続の専門タイプとオーバーライドメソッドを持つクラスを定義しましたか?

+0

「父」を特性に変えることは問題を解決するようですが、その理由はわかりません。 – adamwy

答えて

3

記事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の値をボックスに入れるか、または特性の特殊化サポートを無効にしました。

+0

'trait'のどの特性がこれを機能させますか? –

+0

Scala 2.12では、Scala 2.11では、特性がJava 8のデフォルトメソッドを使い分けているため、Scala 2.12でしか動作しません。そのため、特性が無限再帰を引き起こします。 – adamwy

+0

を参照してください。したがって、あなたの性格がScalaでSAM変換されていない限り、これは変わりません。2.12 –

関連する問題