Dlaczego Java nie wprowadza wnioskowania?

44

Zawsze zastanawiałem się, dlaczego Java nie korzysta z wnioskowania o typie, biorąc pod uwagę, że język jest tym, czym jest, a jego maszyna wirtualna jest bardzo dojrzała. Google's Go jest przykładem języka z doskonałym wnioskowaniem na temat tekstu i ogranicza liczbę operacji pisania. Czy jest jakiś szczególny powód, dla którego ta funkcja nie jest częścią Javy?

cobie
źródło
3
zasady kompatybilności wstecznej w języku tak starym i popularnym jak java
maniak ratchet
9
@ratchetfreak Nie można dodać wnioskowania typu w sposób zgodny z poprzednimi wersjami? Starsze programy po prostu dostarczałyby więcej informacji o typie niż to konieczne.
1
Może mieć pewne niezamierzone efekty, jeśli zostanie użyty z Kasowaniem typu. docs.oracle.com/javase/tutorial/java/generics/…
Zachary Yates
4
Zauważ, że Java 8 przyniesie wiele wnioskowania o typie dzięki funkcji Lambda: możesz pisać złożone lambdy bez wspominania o jakichkolwiek typach i wszystkich wnioskach.
Joachim Sauer
4
Lokalne wnioskowanie o typach zmiennych zbliża się do Java: JEP 286: Wnioskowanie
Jimmy Page

Odpowiedzi:

60

Technicznie rzecz biorąc, Java korzysta z wnioskowania typu podczas korzystania z generycznych. Za pomocą ogólnej metody, takiej jak

public <T> T foo(T t) {
  return t;
}

Kompilator przeanalizuje i zrozumie to podczas pisania

// String
foo("bar");
// Integer
foo(new Integer(42));

Ciąg znaków zostanie zwrócony dla pierwszego wywołania, a liczba całkowita dla drugiego wywołania na podstawie tego, co podano jako argument. W rezultacie otrzymasz odpowiednie sprawdzanie czasu kompilacji. Ponadto w Javie 7 można uzyskać dodatkowe wnioskowanie o typach podczas tworzenia instancji podobnych

Map<String, String> foo = new HashMap<>();

Java jest na tyle uprzejma, aby wypełnić dla nas puste nawiasy kątowe. Dlaczego Java nie obsługuje wnioskowania o typach w ramach przypisywania zmiennych? W pewnym momencie istniało RFE do wnioskowania typu w deklaracjach zmiennych, ale zostało to zamknięte jako „Nie naprawi”, ponieważ

Ludzie korzystają z nadmiarowości deklaracji typu na dwa sposoby. Po pierwsze, nadmiarowy typ służy jako cenna dokumentacja - czytelnicy nie muszą szukać deklaracji getMap (), aby dowiedzieć się, jaki typ zwraca. Po drugie, nadmiarowość pozwala programiście zadeklarować zamierzony typ, a tym samym skorzystać z kontroli krzyżowej przeprowadzanej przez kompilator.

Współautor, który to zamknął, zauważył również, że jest to po prostu „nie-java-podobne”, z którym jestem jednym z nich się zgodzić. Szczegółowość Javy może być zarówno błogosławieństwem, jak i przekleństwem, ale sprawia, że ​​język jest tym, czym jest.

Oczywiście ten konkretny RFE nie był końcem tej rozmowy. W Javie 7 ponownie rozważono tę funkcję , tworząc niektóre implementacje testowe, w tym jedną przez Jamesa Goslinga. Ponownie ta funkcja została ostatecznie zestrzelona.

Wraz z wydaniem Java 8 możemy teraz wnioskować o typie jako część lambd jako takich:

List<String> names = Arrays.asList("Tom", "Dick", "Harry");
Collections.sort(names, (first, second) -> first.compareTo(second));

Kompilator Java może patrzeć sposobu Collections#sort(List<T>, Comparator<? super T>)i interfejsu Comparator#compare(T o1, T o2)i określenia, firstoraz secondpowinno być Stringw ten sposób pozwalając na programator zrezygnować konieczności przekształca typu w wyrażeniu lambda.

Ostry ból
źródło
5
First, the redundant type serves as valuable documentation - readers do not have to search for the declaration of getMap() to find out what type it returns- tak, jeśli tak, to HashMap<String, Integer> map = foo.getMap()zgadzam się - w C # zwykle nie używam varw takich przypadkach, nawet jeśli mógłbym. Ale ten argument nie trzyma wody, jeśli jest HashMap<String, Integer> map = new HashMap<String, Integer>(). To prawdziwa redundancja i nie widzę żadnej korzyści z konieczności dwukrotnego wpisywania nazwy typu. A w jaki sposób skorzystałbym z kontroli krzyżowej kompilatora, nie rozumiem wcale.
Konrad Morawski
9
Tak i to jest dobre dla ogólnych, ale wciąż brakuje mi odpowiednika C # varw Javie.
Konrad Morawski
15
Co do tego, że jest „nie-java-like”, przychodzi na myśl ta (tandetna) historia;) 9gag.com/gag/2308699/-this-is-how-things-are-done-around-here
Konrad Morawski
6
Ponadto, zwracany typ funkcji jest często albo oczywisty, albo niepotrzebny, a nawet w przypadkach, gdy nie jest to twoje IDE może oznaczyć ten typ w pół sekundy.
Phoshi,
8
Uważam, że oficjalny cytat Humans benefit from the redundancyjest prawdziwą odpowiedzią na bieżące pytanie, ponieważ każde uzasadnienie, które przeczytałem, wydaje się nieuzasadnioną, bezzasadną i niedorzeczną wymówką od projektantów Java: zarówno C #, jak i C ++ mają tę funkcję, projektanci C # i C ++ są nie mniej kompetentni niż Java i wszyscy chętnie z niego korzystają. Co powoduje, że programiści Java różnią się od programistów C # lub C ++? Właśnie dlatego zgadzam się z @KonradMorawski: „Oto, jak tu się wszystko robi” ponownie wydaje się być prawdziwym powodem tego za kulisami.
paercebal
16

Po pierwsze, wnioskowanie typu nie ma nic wspólnego z dojrzałością środowiska uruchomieniowego, niezależnie od tego, czy jest to 30-letni procesor, czy maszyna wirtualna, która jest tak nowa, że ​​bity są nadal błyszczące. chodzi o kompilator.

To powiedziawszy, jest dozwolone w przypadku rodzajów ogólnych, a powodem, dla którego nie jest dozwolone w przypadku typów innych niż ogólne, wydaje się być filozofia - nic nie stoi na przeszkodzie, aby projektanci go dodali.

Aktualizacja: wygląda na to, że Java 10 ją obsługuje —- http://openjdk.java.net/jeps/286

jmoreno
źródło
5

O ile wiem, kiedy Java była projektowana na początku lat dziewięćdziesiątych, wnioskowanie o typach nie było tak popularne wśród języków głównego nurtu (ale było to już bardzo dobrze znane pojęcie, np. W ML). Mogę sobie wyobrazić, że wnioskowanie typu prawdopodobnie nie było obsługiwane, ponieważ Java była skierowana do programistów pochodzących z C ++, Pascal lub innych języków głównego nurtu, które jej nie miały (zasada najmniejszego zaskoczenia).

Ponadto jedną z zasad projektowania Java jest pisanie rzeczy w sposób jawny, aby upewnić się, że programista i kompilator rozumieją kod w taki sam sposób: powielanie informacji zmniejsza ryzyko błędów. Oczywiście może być kwestią gustu, czy wpisanie kilku dodatkowych znaków jest warte dodatkowego bezpieczeństwa, jakie zapewnia, ale taka była filozofia projektowania zastosowana w Javie: pisz wyraźnie.

Nie wiem, czy Java będzie w przyszłości rozpoznawać typy, ale IMO byłoby wielką rewolucją dla tego języka (jak wspomniał Glenn Nelson, został opisany jako „nie-java-podobny”), a następnie można rozważyć odrzucenie nazwij Java na rzecz nowej nazwy.

Jeśli chcesz używać języka JVM z wnioskowaniem typu, możesz użyć Scali.

Giorgio
źródło
2

Mogę wymyślić kilka możliwych przyczyn. Jednym z nich jest to, że wyraźne pisanie jest samo-dokumentowaniem. Java ogólnie sprawia, że ​​jest to priorytet nad zwięzłością. Innym powodem mogą być przypadki, gdy typ jest nieco niejednoznaczny. Na przykład, gdy typ lub dowolny podtyp może spełniać procedurę. Powiedzmy, że chcesz użyć Listy, ale ktoś przychodzi i używa metody wyłącznie dla ArrayList. JIT wnioskuje o ArrayList i kontynuuje działanie, nawet jeśli potrzebujesz błędu kompilacji.

nerwowy
źródło
8
W jaki sposób GridBagLayout gridbag = new GridBagLayout (); dodać do własnej dokumentacji? To czyste powtórzenie.
Anders Lindén,
3
Rozróżnia to przypadek, w którym oba typy nie są takie same. Równie łatwo można przypisać instancję podklasy.
jiggy
Let's say you want to use a List, but someone comes along and uses a method exclusive to ArrayList. The JIT would infer an ArrayList and carry on even if you wanted a compilation error- Nie rozumiem tego trochę. Wnioskowanie typu odbywa się w momencie inicjowania zmiennej, a nie wywoływania w niej metody. Czy mógłbyś pokazać przykład, co miałeś na myśli?
Konrad Morawski
2
@KonradMorawski: Zakładam, że ma na myśli, że jeśli metoda zwróci ArrayList, typ zostanie do tego wywnioskowany. Jeśli chcesz traktować typ jako coś innego niż typ zwracany, nie możesz użyć wnioskowania. Nie rozumiem jednak, w jaki sposób może to stanowić problem w bazie kodu w pobliżu rozsądku.
Phoshi,
JIT nie będzie odgrywał żadnej roli w wnioskowaniu typu. wnioskowanie typu jest zjawiskiem czasu kompilacji. a jeśli zmienna jest zadeklarowana jako odniesienie do wykazu, starając się członkom dostęp ArrayList na nim nie będzie typ kontroli i masz błąd kompilacji
sara
0

Jest to sprzeczne z dobrze ugruntowanym zaleceniem, aby deklarować zmienne przy użyciu najbardziej ogólnego interfejsu, który będzie pasował do twoich potrzeb, i inicjować je za pomocą odpowiedniej klasy implementacyjnej, jak w

Collection<String> names = new ArrayList<>();

Efektywnie,

var names = new ArrayList<String>();

jest niczym innym jak cukrem syntaktycznym

ArrayList<String> names = new ArrayList<String>();

Jeśli chcesz, twoje IDE może wygenerować je z new ArrayList<String>()wyrażenia za pomocą „jednego kliknięcia” (refaktoryzuj / stwórz zmienną lokalną), ale pamiętaj, że jest to sprzeczne z zaleceniem „użyj interfejsów”.

Ralf Kleberhoff
źródło