Perché List non è un sottotipo di List ?

StackOverflow https://stackoverflow.com/questions/1240316

Domanda

public void wahey(List<Object> list) {}

wahey(new LinkedList<Number>());

La chiamata al metodo non digitare-check. Non riesco nemmeno a lanciare il parametro nel seguente modo:

wahey((List<Object>) new LinkedList<Number>());

Dalla mia ricerca, ho capito che la ragione per non permettere questo è tipo di sicurezza. Se ci hanno permesso di fare quanto sopra, allora potremmo avere la seguente:

List<Double> ld;
wahey(ld);

All'interno del metodo wahey, potremmo aggiungere alcune stringhe alla lista di input (come parametro mantiene un riferimento List<Object>). Ora, dopo la chiamata al metodo, ld si riferisce ad una lista con un tipo List<Double>, ma la lista attuale contiene alcuni oggetti String!

Questo sembra diversa al modo normale Java funziona senza farmaci generici. Per esempio:

Object o;
Double d;
String s;

o = s;
d = (Double) o;

Quello che stiamo facendo qui è essenzialmente la stessa cosa, tranne che questo passerà controlli a tempo di compilazione e solo non riescono a run-time. La versione con liste non verrà compilato.

Questo mi porta a credere che questo è puramente una decisione di progettazione per quanto riguarda le restrizioni di tipo di farmaci generici. Speravo di ottenere alcuni commenti su questa decisione?

È stato utile?

Soluzione

Quello che state facendo l'esempio "senza farmaci generici" è un cast, che rende chiaro che si sta facendo qualcosa di tipo pericoloso. L'equivalente con i generici potrebbe essere:

Object o;
List<Double> d;
String s;

o = s;
d.add((Double) o);

che si comporta allo stesso modo (compilazione, ma non riesce a runtime). La ragione per non permettere il comportamento che stai chiedendo circa è perché permetterebbe impliciti di tipo-non sicuri azioni, che sono molto più difficili da notare nel codice. Ad esempio:

public void Foo(List<Object> list, Object obj) {
  list.add(obj);
}

Questo sembra perfettamente bene e type-safe fino a quando si chiama in questo modo:

List<Double> list_d;
String s;

Foo(list_d, s);

Che guarda anche type-safe, perché è come il chiamante non necessariamente sa cosa Foo sta andando a che fare con i suoi parametri.

Quindi, in questo caso si dispone di due bit apparentemente type-safe di codice, che insieme finiscono per essere di tipo pericoloso. Questo è male, perché è nascosto e quindi difficile da evitare e più difficili da eseguire il debug.

Altri suggerimenti

Considerate se fosse ...

List<Integer> nums = new ArrayList<Integer>();
List<Object> objs = nums
objs.add("Oh no!");
int x = nums.get(0); //throws ClassCastException

Si sarebbe in grado di aggiungere qualcosa del tipo genitore alla lista, che non può essere quello che era precedentemente dichiarato, che, come l'esempio di cui sopra dimostra, provoca problemi di ogni genere. Così, non è consentito.

Non sono sottotipi di ogni altra causa come funzionano i farmaci generici. Ciò che si vuole è quello di dichiarare la funzione in questo modo:

public void wahey(List<?> list) {}

Poi volontà di accettare una lista di tutto ciò che si estende oggetto. È anche possibile fare:

public void wahey(List<? extends Number> list) {}

Questo vi permetterà di prendere in Elenchi di qualcosa che è una sottoclasse di numero.

Mi raccomando si prende in mano una copia di "Java Generics e collezioni" di Maurice Naftalin & Philip Wadler.

Ci sono essenzialmente due dimensioni di astrazione qui, l'elenco astrazione e l'astrazione del suo contenuto. E 'perfettamente bene al variare lungo l'elenco di astrazione - di dire, per esempio, che si tratta di una LinkedList o un ArrayList - ma non è bene per limitare ulteriormente i contenuti, per dire: questo (lista che contiene gli oggetti) è una (lista collegata che detiene solo numeri). Perché ogni riferimento che conosce come (lista che contiene gli oggetti) capisce, dal contratto di questo tipo, che può contenere qualsiasi oggetto.

Questo è molto diverso da quello che hai fatto nel codice di esempio non generici, in cui hai detto: trattare questa stringa come se fosse un doppio. Si sta invece cercando di dire: il trattamento di questa (lista che contiene solo numeri) come (lista che contiene nulla). E non lo fa, e il compilatore può rilevare, in modo da non farvi scappare con esso.

  

"Quello che stiamo facendo qui è essenzialmente   la stessa cosa, tranne che questo passerà   di compilazione controlli e solo non riescono a   run-time. La versione con liste non lo farà   compilazione ".

Quello che stai osservando ha perfettamente senso se si considera che lo scopo principale di generici Java è quello di ottenere il tipo di incompatibilità a fallire in fase di compilazione, invece di tempo di esecuzione.

java.sun.com

  

Generics fornisce un modo per voi di   comunicare il tipo di una raccolta   al compilatore, in modo che possa essere   controllato. Una volta che il compilatore conosce il   Elemento tipo di raccolta, la   compilatore può controllare che hai usato   la raccolta in modo coerente e può   inserire i calchi corrette sui valori   essere portato fuori della collezione.

In Java, List<S> non è un sottotipo di List<T> quando S è un sottotipo di T. Questa regola fornisce la sicurezza di tipo.

Diciamo che ci permettono un List<String> di essere un sottotipo di List<Object>. Si consideri il seguente esempio:

public void foo(List<Object> objects) {
    objects.add(new Integer(42));
}

List<String> strings = new ArrayList<String>();
strings.add("my string");
foo(strings); // this is not allow in java
// now strings has a string and an integer!
// what would happen if we do the following...??
String myString = strings.get(1);

Quindi, forzandolo fornisce digitare sicurezza, ma ha anche un inconveniente, è meno flessibile. Si consideri il seguente esempio:

class MyCollection<T> {
    public void addAll(Collection<T> otherCollection) {
        ...
    }
}

Ecco una collezione di T di, si consiglia di aggiungere i lotti di un'altra raccolta. Non è possibile chiamare questo metodo con un Collection<S> per un sottotipo di S T. Idealmente, questo è ok perché si sta solo aggiungendo elementi nella vostra collezione, non si sta modificando la raccolta dei parametri.

Per risolvere questo problema, Java offre quello che chiamano "jolly". I caratteri jolly sono un modo di fornire covarianza / controvarianza. Ora considerare quanto segue usando i caratteri jolly:

class MyCollection<T> {
     // Now we allow all types S that are a subtype of T
     public void addAll(Collection<? extends T> otherCollection) {
         ...

         otherCollection.add(new S()); // ERROR! not allowed (Here S is a subtype of T)
     }
} 

Ora, con i caratteri jolly permettiamo covarianza nel tipo T e blocchiamo operazioni che non sono di tipo sicuro (ad esempio l'aggiunta di un elemento nella collezione). In questo modo si ottiene la flessibilità e il tipo di sicurezza.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top