2014-07-01 9 views
17

Następująca klasa zawiera zmienną składową runnable, która jest inicjowana przy użyciu instancji anonimowej klasy wewnętrznej. Wewnętrzna grupa odwołuje się do tego samego członu:Dlaczego lambdas w Javie 8 nie zezwala na przekierowanie do zmiennych składowych, w których klasy anonimowe nie są używane?

class Example { 
    Runnable runnable = new Runnable() { 
     @Override 
     public void run() { 
      System.out.println(runnable); 
     } 
    }; 
} 

nie jest problemem jeśli metoda ta nie jest wykonywana zanim element został przypisany, a na trasę umożliwia takie odniesienie.

Oświadczenie o zmiennej składowej teoretycznie może być przekształcany do ekspresji lambda tak:

Runnable runnable =() -> System.out.println(runnable); 

moim rozumieniu jest funkcjonalnie równoważny do poprzedniego przykładu, lecz jest odrzucana przez javac 1.8.0_05 z następujących komunikat o błędzie:

Error:(2, 54) java: self-reference in initializer 

Chociaż to stwierdzenie jest prawdziwe, nie widzę powodu, dla którego było to zabronione. Czy zostało to celowo zabronione, może dlatego, że wyrażenia lambda są kompilowane do różnych kodów bajtowych, które mogłyby prowadzić do problemów, gdyby było dozwolone? Czy po prostu został odrzucony, ponieważ były już problemy z tymi odniesieniami, gdy były używane w anonimowych klasach wewnętrznych? A może było to niechcący zabronione przez pisarzy JLS? Czy jest to błąd w javac?

+8

W czasie zaćmienia działa: 'Runnable runnable =() -> System.out.println (this.runnable); 'Zawodzi tylko bez dodanego kwalifikatora' this'. –

Odpowiedz

23

Bug #JDK-8027941 opisuje dokładnie to. Dan Smith (Project Lambda Specification Lead) pisze, że to nie jest błąd, a nie tylko lambda.

W komentarzach dotyczących a related bug report, stawia go w ten sposób:

8.3.2.3: Po pierwsze, „użytkowania” w polu inicjatora obszarze są zabronione, jeżeli użycie występuje przed deklaracją pola . Specyfikacja nie jest bardzo jasna, ale zawsze chodziło o to, że "przed" obejmuje inicjator pola. Tak więc "int x = x+1;" nie jest prawidłową deklaracją pola.

Zauważa również:

byłoby możliwe dodać funkcję, która będzie leczyć ciała lambda specjalnie, jak organy anonimowych klas (lub, bardziej ogólnie, pozwalają lambda odnoszą się do siebie jeśli jest to inicjator zmiennej), ale nie zostało to zrobione. (FWIW, prosty uszczypnąć z 8.3.2.3 nie będzie całkowicie bezpieczny, tak jak 4. pocisk nie jest obecnie całkowicie bezpieczne. „Function f = (Function) ((Function) e -> f.apply(e)).apply(null);”)

Myślę, że problemem jest to, że projektanci chcą Java mają proste, syntaktyczne reguły decydujące o tym, jakie rodzaje zdań są dozwolone, a nie zależne od bardziej złożonej, semantycznej analizy kodu. Korzyścią jest prawdopodobnie prostsza specyfikacja, a zatem mniejsze wymagania dla kompilatorów, a kosztem jest to, że programiści nie mogą wyrazić każdego programu - przynajmniej nie w taki sposób, w jaki chcą.


Jak zauważa Marko Topolnik, istnieje rozwiązanie: w pełni zakwalifikować pole. Przykład z raportu o błędzie:

import java.util.function.Function; 

public class LambdaSelfRef { 

    // COMPILATION FAILURE 
    public static Function<Object, Object> op1 = e -> op1.apply(e); 

    // COMPILES OK 
    public static Function<Object, Object> op2 = e -> LambdaSelfRef.op2.apply(e); 

    /* ... */ 
} 
Powiązane problemy