2016-01-19 19 views
7

Sto leggendo OCP Java SE7, certification guide from Mala Gupta. A pagina 297, il seguente frammento di codiceQuali modifiche nell'algoritmo di inferenza del tipo causano questo comportamento?

import java.util.HashMap; 
import java.util.Map; 

public class TestGenericTypeInference { 
    Map<String,Double> salaryMap  = new HashMap<>(); 
    Map<String,Object> copySalaryMap = new HashMap<>(salaryMap); 
} 

sta compilando con Java 8, ma con Java 7 il compilatore si lamenta:

TestGenericTypeInference.java:8: error: incompatible types: HashMap<String,Double> cannot be converted to Map<String,Object> 
    Map<String,Object> copySalaryMap = new HashMap<>(salaryMap); 
           ^

La mia domanda è: Quale cambiamento nell'algoritmo di inferenza di tipo provoca questo comportamento?

risposta

1

penso che è descritto in JLS 8, ch 18.2.1:

Trattando nidificati invocazioni metodo generico come espressioni poli, abbiamo migliorare il comportamento di inferenza per le invocazioni nidificati. Ad esempio, il seguente è illegale in Java SE 7, ma legale in Java SE 8:

ProcessBuilder b = new ProcessBuilder(Collections.emptyList()); // ProcessBuilder's constructor expects a List<String>

Quando sia l'esterno e l'invocazione nidificato richiedono deduzione, il problema è più difficile. Ad esempio:

List<String> ls = new ArrayList<>(Collections.emptyList());

nostro approccio è quello di "sollevamento" i limiti dedurre per l'invocazione nidificata (semplicemente { α <: Object } nel caso di emptyList) nella esterno processo inferenza (in questo caso, cercando di infer β dove il costruttore è per tipo ArrayList<β>). Indichiamo anche le dipendenze tra le variabili di inferenza annidate e le variabili di inferenza esterna (il vincolo ‹List<α> → Collection<β>> ridurrebbe a la dipendenza α = β). In questo modo, la risoluzione delle variabili di inferenza nella chiamata nidificata può attendere fino a quando non si ottengono ulteriori informazioni dall'invocazione esterna (in base al target di assegnazione , β = String).

Anche questo esempio List<String> ls = new ArrayList<>(Collections.emptyList()); non è compilato in java-7.

+0

Ma può davvero 'new HashMap <> (salaryMap);' essere considerato come un'invocazione di metodo generico nidificata? –

+0

La stessa cosa di 'new ArrayList <> (Collections.emptyList()) ', vedere sopra – Andremoniy

+0

Sì, ma' salaryMap' è un riferimento a un oggetto non un'invocazione di metodo che richiede l'inferenza come 'Collections.emptyList'. –

1

Dai un'occhiata alla seguenti link:
https://forums.manning.com/posts/list/36712.page
https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

Il compilatore Java sfrutta tipizzazione bersaglio per inferire i parametri di tipo di una chiamata di metodo generico.

static <T> List<T> emptyList(); 
List<String> listOne = Collections.emptyList(); 

Opere in entrambi.
Tuttavia, questo non è necessario in questo contesto. Era necessario in altri contesti, però.Si consideri il seguente metodo:

void processStringList(List<String> stringList) { 
    // process stringList 
} 

si supponga di voler richiamare il metodo processStringList con una lista vuota. In Java SE 7, la seguente dichiarazione non viene compilato:

processStringList(Collections.emptyList()); 

Il Java SE 7 compilatore genera un messaggio di errore simile al seguente:

List<Object> cannot be converted to List<String> 

Il il compilatore richiede un valore per l'argomento type T, quindi inizia con il valore Object. Di conseguenza, la chiamata di Collections.emptyList restituisce un valore di tipo List, che è incompatibile con il metodo processStringList. Così, in Java SE 7, è necessario specificare il valore del valore dell'argomento tipo come segue:

processStringList(Collections.<String>emptyList()); 

Questo non è più necessaria in Java SE 8. La nozione di ciò che è un obiettivo il tipo è stato espanso per includere gli argomenti del metodo, come l'argomento del metodo processStringList. In questo caso, processStringList richiede un argomento di tipo List < String>. Il metodo Collections.emptyList restituisce un valore di List < T>, quindi utilizzando il tipo di destinazione di List < String>, il compilatore deduce che l'argomento di tipo T ha un valore di String. Così, in Java SE 8, la seguente dichiarazione compila:

processStringList(Collections.emptyList()); 
0

La risposta alla mia domanda:

Cosa cambiamento algoritmo di inferenza provoca questo comportamento?

è in the Generics FAQ from Angelina Langer. Un esempio simile è dato:

// error in Java 7 ; fine since Java 8 
Set<Number> s3 = new HashSet<>(Arrays.asList(0L,0L)); 
  • Il [...] espressione dimostra che il lato sinistro della cessione è infatti ignorato (in Java 7). Il compilatore deduce ancora dall'argomento dei costruttori, cioè il risultato del metodo asList, che il parametro di tipo mancante per il nuovo HashSet deve essere Long. Ciò comporta una mancata corrispondenza del tipo e un messaggio di errore corrispondente. Il compilatore non conclude che il parametro di tipo mancante dovrebbe essere Numero perché ignora il lato sinistro del compito. In Java 8, l'inferenza del tipo è stata modificata e migliorata. Da allora, il compilatore diventa Number come il parametro type del nuovo HashSet sul lato destro del compilatore e da questo deduce Number come parametro type per il metodo asList. In Java 8, questo si compila bene.