Wnioskowanie typu w Javie 8

30

Czy wprowadzenie nowej notacji lambda (patrz np. Ten artykuł ) w Javie 8 będzie wymagało pewnego rodzaju wnioskowania typu?

Jeśli tak, to w jaki sposób nowy system typów wpłynie na język Java jako całość?

Giorgio
źródło

Odpowiedzi:

47

Jest sporo niepoprawnych informacji w odpowiedzi maniaka grzechotki i w wątku komentarza. Odpowiem tutaj, ponieważ komentarz jest za mały. Ponadto, skoro to odpowiedź, w końcu spróbuję odpowiedzieć na pierwotne pytanie. (Zauważ jednak, że nie jestem ekspertem od systemów typu).

Po pierwsze, krótkie odpowiedzi na pierwotne pytanie brzmią: tak i nie. Tak, Java 8 będzie miała znacznie więcej wnioskowania o typie niż Java 7, i nie, nie ma „nowego” systemu typów w Javie 8, chociaż są pewne drobne zmiany .

Java 8 nadal będzie typowana statycznie i nadal będzie miała dychotomię między klasami i interfejsami. Nie ma żadnych nowych typów, takich jak typy funkcji. Typ lambda jest zasadniczo „interfejsem funkcjonalnym”, który jest zwykłym interfejsem z pojedynczą metodą abstrakcyjną.

Interfejsy mogą teraz mieć kod w postaci metod domyślnych, ale model pojedynczego dziedziczenia klas i wielokrotnego dziedziczenia interfejsów pozostaje taki sam. Oczywiście są pewne korekty, takie jak reguły rozwiązywania metod w obecności metod domyślnych, ale podstawy pozostają niezmienione.

Każdy typ, który można wywnioskować na podstawie wnioskowania o typie, może zostać zapisany jawnie. Aby użyć przykładu maniaka ratchet ,

Collections.<MyClass>sort(list, (a, b) -> { return a.order - b.order; });

jest w zasadzie cukrem dla

Collections.<MyClass>sort(list,
    (Comparator<MyClass>)((MyClass a, MyClass b) -> { return a.order - b.order; }));

Zatem stwierdzenie sparkleshy „wnioskowanie typu nie wymaga żadnego rozszerzenia systemu typów” jest w zasadzie poprawne.

Ale wracając do cukru syntaktycznego, powtórzę moje stwierdzenie, że wyrażenie lambda nie jest cukrem syntaktycznym dla anonimowej klasy wewnętrznej. Dziwak Ratchet stwierdził, że wyrażenie lambda jest tłumaczone na anonimową instancję klasy wewnętrznej, a Sparkleshy po prostu potwierdził, że lambda jest cukrem syntaktycznym dla anonimowej klasy wewnętrznej, ale te stwierdzenia są niepoprawne. Prawdopodobnie są oparte na nieaktualnych informacjach. Wczesne implementacje lambda wprowadzały lambdy w ten sposób, ale wszystko się zmieniło.

Wyrażenia lambda różnią się semantycznie od klas wewnętrznych i są implementowane inaczej niż klasy wewnętrzne.

Wyrażenia lambda różnią się semantycznie od klas wewnętrznych na kilka sposobów. Ocena wyrażenia lambda nie musi za każdym razem tworzyć nowej instancji. Mają także inną semantykę przechwytywania, na przykład przechwytują to inaczej. W wewnętrznej klasy, to jest wewnętrzną przykład klasy, natomiast w lambda, to jest to przypadek obejmujących. Rozważ następujące:

public class CaptureThis {
    void a(Runnable r) { r.run(); }

    void b() {
        a(new Runnable() { public void run() { System.out.println(this); }});
        a(() -> System.out.println(this));
    }

    public String toString() { return "outer"; }

    public static void main(String[] args) { new CaptureThis().b(); }
}

W najnowszej wersji JDK 8 lambda (użyłem b69 ), wynik będzie podobny do następującego:

CaptureThis$1@113de03
outer

Ponadto wyrażenia lambda są implementowane zupełnie inaczej niż klasy wewnętrzne. Jeśli porównasz zdemontowane dane wyjściowe, zobaczysz, że kod klasy wewnętrznej bezpośrednio kompiluje się do stworzenia i wywołuje konstruktor CaptureThis 1 USD, podczas gdy wyrażenie lambda kompiluje się do wywołanej instrukcji dynamicznej, która wywołuje Runnable w sposób nieokreślony. Pełne wyjaśnienie tego, jak to działa i dlaczego, znajduje się w wykładzie JavaOne 2012 Briana Goetza Lambda: A Peek Under The Hood .

Znaki Stuarta
źródło
2
„wychwytują to inaczej. W klasie wewnętrznej jest to instancja klasy wewnętrznej, podczas gdy w lambda jest to instancja zamykająca. Zastanów się, co następuje:” nadal można to zrobić z cukrem syntaktycznym, zastępując wszystko thisprzezMyClass.this
maniakiem zapadkowym
4
Wszystko, co wykracza poza asembler (a czasem nawet to), to prawdopodobnie cukier składniowy. Ale lambda niejuż cukrem syntaktycznym dla klas wewnętrznych (już więcej). Służą podobnemu celowi i mają pewne podobieństwa, ale pod maską są (zauważalnie) różne.
Joachim Sauer
1
„Wyrażenia lambda różnią się semantycznie od klas wewnętrznych i są realizowane inaczej niż klasy wewnętrzne.”: Dziękujemy za bardzo dobre wyjaśnienie (+1). Nadal nie jest dla mnie jasne, dlaczego lambd nie zostały zaimplementowane jako cukier syntaktyczny dla specjalnych anonimowych klas lub, innymi słowy, jaka jest zaleta dodatkowej złożoności wprowadzonej przez nową semantykę? Jaki problem rozwiązuje ten problem, którego nie można rozwiązać za pomocą anonimowych klas wewnętrznych? (Ale nadal mam patrzeć na link, który pisał, może znajdę tam odpowiedzi.)
Giorgio
1
@Jachach Sauer: Uważam cukier syntaktyczny za nową składnię dla istniejącej semantyki. Kiedy wprowadza się nową semantykę, myślę, że nie można mówić o cukrze syntaktycznym. W tym sensie nie sądzę, że można argumentować, że wszystko poza asemblerem to cukier składniowy.
Giorgio
3
@Giorgio: jeśli chodzi o to, dlaczego lambda nie jest tylko cukrem syntaktycznym dla anonimowych klas wewnętrznych, okazuje się, że była na ten temat duża dyskusja. Ta poczta z Brian Goetz podsumowuje decyzję: mail.openjdk.java.net/pipermail/lambda-dev/2011-August/... . TL; DR pozostawia otwarte drzwi dla przyszłej ewolucji i dzisiaj uzyskujemy lepszą wydajność. Goetz odpowiada na podobne pytanie: Czy obiekty lambdas? Ale jeśli odpowiedź brzmi „nie” lub „może”, oznacza to, że nie mogą być cukrem dla klas wewnętrznych.
Stuart Marks