Perché Java non digita l'inferenza?


44

Mi sono sempre chiesto perché Java non fa l'inferenza del tipo dato che il linguaggio è quello che è e la sua VM è molto matura. Google's Go è un esempio di una lingua con un'eccellente deduzione dei caratteri e riduce la quantità di digitazione che uno deve fare. C'è qualche motivo speciale dietro questa funzione che non fa parte di Java?


3
regole di retrocompatibilità in una lingua antica e popolare come java
ratchet maniaco

9
@ratchetfreak Impossibile dedurre l'inferenza in modo compatibile con le versioni precedenti? I programmi meno recenti fornirebbero solo più informazioni sul tipo del necessario.

1
Potrebbe avere effetti indesiderati se utilizzato con la cancellazione del tipo. docs.oracle.com/javase/tutorial/java/generics/…
Zachary Yates

4
Nota che Java 8 porterà molta inferenza di tipo con la sua funzionalità Lambda: puoi scrivere lambda complesse senza menzionare alcun tipo e tutto inferito.
Joachim Sauer,

4
L'inferenza di tipo variabile locale sta arrivando a Java: JEP 286: Inferenza di tipo variabile locale
Jimmy Pagina

Risposte:


60

Tecnicamente parlando, Java ha inferenza di tipo quando si usano i generici. Con un metodo generico come

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

Il compilatore lo analizzerà e lo capirà quando scrivi

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

Verrà restituita una stringa per la prima chiamata e un numero intero per la seconda chiamata in base a ciò che è stato inserito come argomento. Di conseguenza, si otterrà il corretto controllo in fase di compilazione. Inoltre, in Java 7, si può ottenere qualche tipo di inferenza quando si istanziano generici in questo modo

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

Java è abbastanza gentile da riempire le parentesi angolari vuote per noi. Ora perché Java non supporta l' inferenza del tipo come parte dell'assegnazione delle variabili? A un certo punto, c'era una RFE per l'inferenza di tipo nelle dichiarazioni di variabili, ma questa è stata chiusa come "Non risolverà" perché

Gli esseri umani beneficiano della ridondanza della dichiarazione di tipo in due modi. Innanzitutto, il tipo ridondante funge da preziosa documentazione: i lettori non devono cercare la dichiarazione di getMap () per scoprire quale tipo restituisce. In secondo luogo, la ridondanza consente al programmatore di dichiarare il tipo previsto e quindi beneficiare di un controllo incrociato eseguito dal compilatore.

Il collaboratore che ha chiuso questo ha anche notato che sembra "non-java-like", con cui sono d'accordo. La verbosità di Java può essere sia una benedizione che una maledizione, ma rende il linguaggio quello che è.

Naturalmente quel particolare RFE non era la fine di quella conversazione. Durante Java 7, questa funzionalità è stata nuovamente considerata , con alcune implementazioni di test create, tra cui una dello stesso James Gosling. Ancora una volta, questa funzionalità è stata infine eliminata.

Con il rilascio di Java 8, ora otteniamo l'inferenza del tipo come parte di lambdas in quanto tale:

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

Il compilatore Java è in grado di esaminare il metodo Collections#sort(List<T>, Comparator<? super T>)e quindi l'interfaccia di Comparator#compare(T o1, T o2)e determinarlo firste seconddovrebbe essere un Stringmodo per consentire al programmatore di rinunciare a dover riformulare il tipo nell'espressione lambda.


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- sì, se è HashMap<String, Integer> map = foo.getMap()allora sono d'accordo - in C # in genere non lo uso varin questi casi, anche se potrei. Ma questo argomento non trattiene l'acqua se lo è HashMap<String, Integer> map = new HashMap<String, Integer>(). Questa è vera ridondanza e non vedo alcun vantaggio nel dover scrivere due volte il nome del tipo. E come trarrebbe beneficio da un controllo incrociato del compilatore, non capisco affatto.
Konrad Morawski,

9
Sì, va bene per i generici, ma mi manca ancora l'equivalente di C # varin Java.
Konrad Morawski,

15
Per quanto sia "un-java-like", mi viene in mente questa storia (di formaggio);) 9gag.com/gag/2308699/-this-is-how-things-are-done-around-here
Konrad Morawski

6
Inoltre, il tipo restituito di una funzione è spesso ovvio o non necessario, e anche nei casi in cui non è il tuo IDE può contrassegnare il tipo in mezzo secondo.
Phoshi,

8
Credo che la citazione ufficiale Humans benefit from the redundancysia la vera risposta alla domanda attuale, perché ogni giustificazione che ho letto sembra una scusa gratuita, infondata e ridicola da parte dei progettisti Java: Sia C # che C ++ hanno la caratteristica, i progettisti C # e C ++ non sono meno competenti di Java, e tutti sono felici di usarlo. Che cosa potrebbe fare in modo che gli sviluppatori Java siano diversi dagli sviluppatori C # o C ++? Questo è il motivo per cui sono d'accordo con @KonradMorawski, "Ecco come vengono fatte le cose qui" sembra di nuovo essere la vera ragione del dietro le quinte.
paercebal,

16

Bene, prima di tutto, l'inferenza del tipo non ha nulla a che fare con la maturità del runtime, sia che il runtime sia una CPU di 30 anni o una VM così nuova che i bit siano ancora brillanti. si tratta del compilatore.

Detto questo, è consentito per i generici, il motivo per cui non è consentito per i tipi non generici sembra essere dovuto alla filosofia - non c'è nulla che impedisce ai designer di aggiungerlo.

Aggiornamento: sembra che Java 10 lo supporti —- http://openjdk.java.net/jeps/286


5

Per quanto ne so, quando Java fu progettato all'inizio degli anni '90, l'inferenza del tipo non era così popolare tra i linguaggi tradizionali (ma era già un concetto molto noto, ad esempio in ML). Quindi, posso immaginare che l'inferenza del tipo probabilmente non era supportata perché Java era indirizzato a programmatori provenienti da C ++, Pascal o altri linguaggi tradizionali che non lo avevano (principio della minima sorpresa).

Inoltre, uno dei principi di progettazione di Java è scrivere esplicitamente le cose per assicurarsi che il programmatore e il compilatore abbiano la stessa comprensione del codice: la duplicazione delle informazioni riduce la possibilità di errori. Certo, può essere una questione di gusti se scrivere qualche altro carattere valga la sicurezza aggiuntiva che fornisce, ma questa è stata la filosofia progettuale seguita per Java: scrivere esplicitamente.

Non so se Java otterrà l'inferenza del tipo in futuro, ma IMO sarebbe una grande rivoluzione per il linguaggio (come ha detto Glenn Nelson, è stato descritto come "un-java-like") e quindi si potrebbe anche considerare di abbandonare il nome Java a favore di un nuovo nome.

Se si desidera utilizzare un linguaggio JVM con inferenza di tipo, è possibile utilizzare Scala.


2

Posso pensare ad alcune possibili ragioni. Uno è che la digitazione esplicita è auto-documentazione. Java generalmente lo rende una priorità rispetto alla concisione. Un altro motivo potrebbe essere nei casi in cui il tipo è alquanto ambiguo. Come quando un tipo o qualsiasi sottotipo potrebbe soddisfare una routine. Supponiamo che tu voglia usare un Elenco, ma qualcuno arriva e usa un metodo esclusivo di ArrayList. La JIT avrebbe inferito una ArrayList e avrebbe continuato anche se si voleva un errore di compilazione.


8
Come funziona GridBagLayout gridbag = new GridBagLayout (); aggiungere alla documentazione personale? La sua pura ripetizione.
Anders Lindén,

3
Si chiarisce da un caso in cui i due tipi non sono uguali. Potresti allo stesso modo assegnare un'istanza di una sottoclasse.
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- Non capisco questo bit. L'inferenza del tipo ha luogo al momento di istatare una variabile, senza richiamare un metodo su di essa. Potresti forse mostrare un esempio di ciò che intendevi?
Konrad Morawski,

2
@KonradMorawski: suppongo significhi che se un metodo restituisce ArrayList, il tipo verrà dedotto a quello. Se si desidera trattare un tipo come qualcosa di diverso dal tipo restituito, non è possibile utilizzare l'inferenza. Tuttavia, non capisco come questo possa mai essere un problema in una base di codice quasi sana.
Phoshi,

la squadra non avrebbe avuto alcun ruolo nell'inferenza del tipo. l'inferenza del tipo è un fenomeno in fase di compilazione. e se una variabile viene dichiarata come riferimento a un Elenco, tentando di accedere ai membri di ArrayList su di essa non si digita check e si ottiene un errore di compilazione
sara

0

Contraddice la consolidata raccomandazione di dichiarare le variabili utilizzando l'interfaccia più generica adatta alle tue esigenze e inizializzarle con una classe di implementazione appropriata, come in

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

In effetti,

var names = new ArrayList<String>();

non è altro che zucchero sintattico per

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

Se lo desideri, il tuo IDE può produrlo new ArrayList<String>()dall'espressione con "un clic" (refactor / create variabile locale), ma ricorda che contraddice la raccomandazione "usa interfacce".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.