2016-12-26 89 views
2

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

那麼,如何重用超類的專門註釋的方法?

+0

把'Father'變成特質似乎解決了這個問題,但我不確定這個原因。 – adamwy

回答

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$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方法,因爲它沒有得到真正的專業化公頃s來打包get$mcI$sp中的值,這可能是一個錯誤,或者Scala 2.12中可能會丟失對特徵的專業化支持。

+0

「特質」的哪些屬性使這項工作成爲可能? –

+0

它使得它僅適用於Scala 2.12(因爲特徵使用了不同處理的Java 8默認方法),因此在Scala 2.11中使它成爲一個特性也會導致無限遞歸。 – adamwy

+0

我明白了。所以這種情況會持續下去,除非你的特質在Scala中轉換爲SAM 2.12 –