2016-12-26 3 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

Превращение «Отца» в черту, похоже, решает проблему, но я не уверен в причине этого. – 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

Какие свойства 'trait' делают эту работу? –

+0

Это заставляет работать только с Scala 2.12 (потому что черты используют методы по умолчанию Java 8, которые обрабатываются по-разному), в Scala 2.11, что делает его чертой, вызывает бесконечную рекурсию. – adamwy

+0

Я вижу.Таким образом, это сохраняется, если ваша черта не преобразуется в SAM в Scala. 2.12. –

Смежные вопросы