2013-02-16 6 views
12

List<String> list = new ArrayList(); visualizzerà l'avviso del compilatore.Perché l'operatore diamond viene utilizzato per il tipo Inference in Java 7?

Tuttavia il seguente esempio compila senza alcun preavviso: List<String> list = new ArrayList<>();

Sono curioso di sapere il motivo per cui l'introduzione dell'operatore diamante è necessaria a tutti. Perché non basta avere inferenza di tipo sul costruttore se il tipo di argomento è assente (come già fatto per i metodi statici in Java e sfruttata da librerie di raccolta come google guava)

EDIT: Usando risposta millimoose come punto di partenza ho guardato che tipo di cancellazione è effettivamente e non è solo rimuovendo tutte le informazioni sul tipo. Compiler in realtà fa un po 'di più (copiato da official doc):

  • Sostituire tutti i parametri di tipo di tipi generici con i loro limiti o di un oggetto se i parametri di tipo sono illimitati. Il codice prodotto, quindi, contiene solo classi, interfacce e metodi ordinari.
  • Inserire tipi di cast, se necessario, per preservare la sicurezza del tipo.
  • Generare metodi bridge per preservare il polimorfismo in tipi generici estesi.
+7

sto supponendo che sia per distinguere questo usare i tipi prime, che rendono il compilatore fare qualcosa diverso complessivamente per motivi di compatibilità. (Un'espressione con un tipo non elaborato viene elaborata in modo diverso rispetto a quella che riguarda i generici.) – millimoose

+0

Probabilmente sarei d'accordo con millimoose, ma dato che i generici vengono cancellati durante il runtime introducendo un nuovo operatore ('solo') per la retrocompatibilità dell'avvertimento del compilatore non mi sembra giusto –

+0

La compatibilità con le versioni precedenti non è per gli avvisi del compilatore. Una dichiarazione come 'Object s = new ArrayList(). Get()' ha il tipo di risultato del lato destro risolto in modo diverso (usando l'algoritmo pre-generico) che dice 'String s = new ArrayList () .get() '. Questo vale anche se si memorizza 'ArrayList' in una variabile intermedia. – millimoose

risposta

8

La risposta definitiva dovrebbe provenire da qualcuno che ha progettato tale funzione, ma sto assumendo che sia da distinguere dall'uso di tipi non elaborati, che rendono il compilatore qualcosa di completamente diverso per motivi di compatibilità. Un'espressione con un tipo grezzo in esso viene elaborato sottilmente diverso rispetto uno che coinvolge generici, un esempio si trova in questa domanda SO: Generic screws up non-related collection

+0

+1 per dare un esempio della differenza tra generici e tipi grezzi –

+0

@PhilippWendler Avere quella domanda nella memoria recente è davvero ciò che mi porta a questa risposta, quindi parte del merito va anche all'autore. – millimoose

3

Questo fa parte di un miglioramento per Java Generics in Java 7.
Prima avresti dovuto scrivere

final List<String> list = new ArrayList<String>(); 

Ora è possibile scrivere

final List<String> list = new ArrayList<>(); 

che è equivalente - il il compilatore funzionerà per te. Questa non è la stessa di

final List list = new ArrayList(); 

che è un non tipizzato List.

7

Gli sviluppatori Java si sforzano molto per evitare di modificare il comportamento dei programmi esistenti. List<String> list = new ArrayList(); si compila e crea un ArrayList non elaborato. Se l'inferenza di tipo è stata applicata ad esso il risultato sarebbe un ArrayList<String>, cambiando il suo comportamento e probabilmente causando errori di runtime altrove nel programma.

========================================= ================

Dopo un'ulteriore considerazione e un commento di @millimoose, vedo che i cambiamenti nel comportamento sarebbe locale per l'inizializzatore e rilevato al momento della compilazione. Si consideri il seguente programma:

import java.util.ArrayList; 
import java.util.List; 


public class Test { 
    public static void main(String[] args) throws Exception { 
    List<Integer> integers = new ArrayList<Integer>(); 
    integers.add(Integer.valueOf(3)); 
    integers.add(Integer.valueOf(4)); 
    List<String> list = new ArrayList(integers); 
    System.out.println(list); 
    } 
} 

Senza inferenza di tipo, corre e stampe [3, 4], nonostante la situazione indesiderabile di una List<String> che contiene riferimenti Integer.

Con l'inferenza di tipo, non viene compilato poiché lo ArrayList(Collection<? extends E> c) non consente l'utilizzo di un argomento List<Integer> durante la creazione di ArrayList<String>.

+0

@millimoose Dopo averci pensato di più, sono d'accordo. Le modifiche nel comportamento sarebbero localizzate nell'inizializzatore e rilevate al momento della compilazione. Modificherò la mia risposta per mostrare un esempio. –

4

La piena sintassi richiesta dal compilatore Java 5 e 6 è:

List<String> list = new ArrayList<String>();

Hanno deciso di semplificare la sintassi per noi e permettono non scrivere gli stessi parametri di tipo su entrambi i lati operatore di assegnazione. Tuttavia l'operatore <> è ancora necessario per assicurarsi di aver capito cosa stai facendo. Scrivendo new ArrayList<>() dici "Capisco che sto creando un'istanza di tipo generico e il parametro generico è quello che ho dichiarato sul lato sinistro del compito."

1

I casi interessanti sono dove chiamando il costruttore con il diamante e come rawtype compila con successo ma produce codice diverso. Tali esempi sono possibili se combinati con la funzione di sovraccarico del metodo. IIRC, c'è stato un esempio da qualche parte sulla mailing list delle monete OpenJDK (no, non ho intenzione di provare a trovarlo).

Non era accettabile avere esattamente lo stesso codice venuto correttamente su Java SE 6 e Java SE 7 ma produrre risultati diversi.

Per i miei soldi, avrei omesso il diamante e dato un avvertimento (trattatelo come un errore) se un codice diverso sarebbe stato prodotto dall'algoritmo di inferenza scelto in 7 (essenzialmente lo stesso del metodo di inferenza di tipo generico da J2SE 5.0). Se hai scritto questo codice, probabilmente non è ovvio lavorare se è compilabile o meno.

0

Se il progetto è basato su Maven, aggiungi il sotto sotto pom.xml sotto il tag. Funziona perfettamente .. <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins>