Differenza tra Elenco, Elenco <?>, Elenco <T>, Elenco <E> ed Elenco <Oggetto>


194

Quali sono le differenze tra List, List<?>, List<T>, List<E>, e List<Object>?

1. Elenco

List: è un tipo grezzo, quindi no typesafe. Genererà un errore di runtime solo quando il casting è difettoso. Vogliamo un errore di tempo di compilazione quando il cast è cattivo. Non è raccomandato l'uso.

2. Elenco <?>

List<?>è un carattere jolly senza limiti. Ma non sono sicuro a cosa serva? Posso stampare List<?>senza problemi:

public static void test(List<?> list){
    System.out.println(list);   // Works
}

Perché non riesco ad aggiungere elementi a List<?>?

public static void test(List<?> list){
    list.add(new Long(2));     // Error
    list.add("2");             // Error
    System.out.println(list);
}

3. Elenco <T>

public static void test(List<T> list){   // T cannot be resolved
    System.out.println(list);
}

Non capisco questa sintassi. Ho visto qualcosa del genere, e funziona:

public <T> T[] toArray(T[] a){
    return a;   
}

A volte, vedo <T>, o <E>, o <U>, <T,E>. Sono tutti uguali o rappresentano qualcosa di diverso?

4. Elenco <Oggetto>

Questo dà l'errore "Il metodo test(List<Object>)non è applicabile per l'argomento List<String>":

public static void test(List<Object> list){
    System.out.println(list);
}

Se provo questo, ottengo "Impossibile List<String>eseguire il cast da a List<Object>":

test((List<Object>) names);

Sono confuso. Stringè una sottoclasse di Object, quindi perché non List<String>una sottoclasse di List<Object>?

Risposte:


77

1) corretto

2) Puoi pensare a quello come a un elenco di "sola lettura", in cui non ti interessa il tipo di elementi. Ad esempio, potrebbe essere utilizzato da un metodo che restituisce la lunghezza dell'elenco.

3) T, E e U sono uguali, ma le persone tendono ad usare ad es. T per tipo, E per elemento, V per valore e K per chiave. Il metodo che compila dice che ha preso una matrice di un certo tipo e restituisce una matrice dello stesso tipo.

4) Non puoi mescolare arance e mele. Saresti in grado di aggiungere un oggetto al tuo elenco di stringhe se potessi passare un elenco di stringhe a un metodo che prevede elenchi di oggetti. (E non tutti gli oggetti sono stringhe)


2
+1 per l'elenco di sola lettura su 2. Scrivo alcuni codici per dimostrarlo 2. tyvm
Thang Pham,

Perché le persone dovrebbero usare List<Object>?
Thang Pham,

3
È un modo per creare un elenco che accetta qualsiasi tipo di elementi, raramente lo usi.
Kaj,

In realtà non penserei che qualcuno lo userebbe considerando che non puoi avere un identificatore adesso.
if_zero_equals_one,

1
@if_zero_equals_one Sì, ma riceveresti un avviso del compilatore (avviserebbe e direbbe che stai usando tipi non elaborati) e non vorrai mai compilare il tuo codice con avvisi.
Kaj,

26

Per l'ultima parte: sebbene String sia un sottoinsieme di Object, ma List <String> non è ereditato da List <Object>.


11
Ottimo punto; molti ipotizzano che, poiché la classe C eredita dalla classe P, anche la Lista <C> eredita dalla Lista <P>. Come hai sottolineato, non è così. Il motivo era che se potessimo eseguire il cast da List <String> a List <Object>, allora potremmo mettere gli oggetti in tale elenco, violando così il contratto originale di List <String> quando tentiamo di recuperare un elemento.
Peter,

2
+1. Buon punto anche. Quindi perché le persone dovrebbero usare List<Object>?
Thang Pham,

9
Elenco <oggetto> può essere utilizzato per memorizzare un elenco di oggetti di diverse classi.
Farshid Zaker,

20

La notazione List<?>significa "un elenco di qualcosa (ma non sto dicendo cosa)". Poiché il codice in testfunziona per qualsiasi tipo di oggetto nell'elenco, questo funziona come parametro di metodo formale.

L'uso di un parametro type (come nel punto 3), richiede che il parametro type sia dichiarato. La sintassi Java per questo è di mettere <T>davanti alla funzione. Ciò è esattamente analogo alla dichiarazione di nomi di parametri formali in un metodo prima di utilizzare i nomi nel corpo del metodo.

Riguardo a List<Object>non accettare a List<String>, questo ha senso perché a Stringnon lo è Object; è una sottoclasse di Object. La correzione è dichiarare public static void test(List<? extends Object> set) .... Ma poi extends Objectè ridondante, perché ogni classe si estende direttamente o indirettamente Object.


Perché le persone dovrebbero usare List<Object>?
Thang Pham,

10
Penso che "un elenco di qualcosa" sia un significato migliore List<?>poiché l'elenco è di un tipo specifico ma sconosciuto. List<Object>sarebbe davvero "un elenco di qualsiasi cosa" poiché può effettivamente contenere qualsiasi cosa.
ColinD,

1
@ColinD - Intendevo "qualsiasi cosa" nel senso di "qualsiasi cosa". Ma hai ragione; significa "un elenco di qualcosa, ma non ho intenzione di dirti cosa".
Ted Hopp,

@ColinD voleva dire perché hai ripetuto le sue parole? sì, è stato scritto con parole un po 'diverse, ma il significato è lo stesso ...
user25

14

La ragione per cui non puoi lanciare List<String>a List<Object>è che permetterebbe di violare i vincoli del List<String>.

Pensa al seguente scenario: Se ne ho uno List<String>, dovrebbe contenere solo oggetti di tipo String. (Che è una finalclasse)

Se riesco a trasmetterlo a a List<Object>, ciò mi consente di aggiungere Objecta tale elenco, violando così il contratto originale di List<String>.

Quindi, in generale, se la classe Ceredita dalla classe P, non si può dire che GenericType<C>anche la eredita GenericType<P>.

NB Ho già commentato questo in una risposta precedente, ma volevo ampliarlo.


tyvm, carico sia il tuo commento che la tua risposta, poiché è un'ottima spiegazione. Ora dove e perché le persone dovrebbero usare List<Object>?
Thang Pham,

3
Generalmente non dovresti usare List<Object>come una sorta di sconfitta lo scopo dei generici. Tuttavia, ci sono casi in cui il vecchio codice potrebbe avere Listun'accettazione di tipi diversi, quindi potresti voler aggiornare il codice per utilizzare la parametrizzazione del tipo solo per evitare avvisi del compilatore per i tipi non elaborati. (Ma la funzionalità è invariata)
Peter


5

Parliamo di loro nel contesto della storia di Java;

  1. List:

Elenco significa che può includere qualsiasi oggetto. L'elenco era nella versione precedente a Java 5.0; Java 5.0 ha introdotto List, per compatibilità con le versioni precedenti.

List list=new  ArrayList();
list.add(anyObject);
  1. List<?>:

?significa Oggetto sconosciuto e non Oggetto; l' ?introduzione con caratteri jolly serve a risolvere il problema creato dal tipo generico; vedere i caratteri jolly ; ma questo causa anche un altro problema:

Collection<?> c = new ArrayList<String>();
c.add(new Object()); // Compile time error
  1. List< T> List< E>

Significa Dichiarazione generica con la premessa di nessuno tipo T o E nel progetto Lib.

  1. List< Object> significa parametrizzazione generica.

5

Nel terzo punto, "T" non può essere risolto perché non è dichiarato, di solito quando si dichiara una classe generica è possibile utilizzare "T" come nome del parametro del tipo associato , molti esempi online, inclusi i tutorial di Oracle, usano "T" come nome del parametro type, ad esempio, si dichiara una classe come:

public class FooHandler<T>
{
   public void operateOnFoo(T foo) { /*some foo handling code here*/}

}

stai dicendo che il FooHandler's operateOnFoometodo prevede una variabile di tipo "T" che è dichiarata sulla stessa dichiarazione di classe, con questo in mente, puoi successivamente aggiungere un altro metodo come

public void operateOnFoos(List<T> foos)

in tutti i casi T, E o U ci sono tutti gli identificatori del parametro type, puoi anche avere più di un parametro type che usa la sintassi

public class MyClass<Atype,AnotherType> {}

nel tuo quarto ponint sebbene efficacemente Sting sia un sottotipo di oggetto, nelle classi generiche non esiste una relazione del genere, List<String>non è un sottotipo di List<Object>essi sono due tipi diversi dal punto di vista del compilatore, questo è meglio spiegato in questo post di blog


5

Teoria

String[] può essere lanciato su Object[]

ma

List<String>non è possibile eseguire il cast List<Object>.

Pratica

Per gli elenchi è più sottile di così, perché in fase di compilazione il tipo di un parametro List passato a un metodo non viene verificato. La definizione del metodo potrebbe anche dire List<?>- dal punto di vista del compilatore è equivalente. Questo è il motivo per cui l'esempio n. 2 dell'OP fornisce errori di runtime e non errori di compilazione.

Se List<Object>gestisci attentamente un parametro passato a un metodo in modo da non forzare un controllo del tipo su alcun elemento dell'elenco, puoi definire il tuo metodo usando List<Object>ma in realtà accettare un List<String>parametro dal codice chiamante.

R. Quindi questo codice non darà errori di compilazione o di runtime e funzionerà (e forse sorprendentemente?):

public static void main(String[] args) {
    List argsList = new ArrayList<String>();
    argsList.addAll(Arrays.asList(args));
    test(argsList);  // The object passed here is a List<String>
}

public static void test(List<Object> set) {
    List<Object> params = new ArrayList<>();  // This is a List<Object>
    params.addAll(set);       // Each String in set can be added to List<Object>
    params.add(new Long(2));  // A Long can be added to List<Object>
    System.out.println(params);
}

B. Questo codice genererà un errore di runtime:

public static void main(String[] args) {
    List argsList = new ArrayList<String>();
    argsList.addAll(Arrays.asList(args));
    test1(argsList);
    test2(argsList);
}

public static void test1(List<Object> set) {
    List<Object> params = set;  // Surprise!  Runtime error
}

public static void test2(List<Object> set) {
    set.add(new Long(2));       // Also a runtime error
}

C. Questo codice genererà un errore di runtime ( java.lang.ArrayStoreException: java.util.Collections$UnmodifiableRandomAccessList Object[]):

public static void main(String[] args) {
    test(args);
}

public static void test(Object[] set) {
    Object[] params = set;    // This is OK even at runtime
    params[0] = new Long(2);  // Surprise!  Runtime error
}

In B, il parametro setnon è un tipo Listal momento della compilazione: il compilatore lo vede come List<?>. C'è un errore di runtime perché in fase di runtime, setdiventa l'oggetto reale passato da main(), e questo è un List<String>. A List<String>non può essere lanciato List<Object>.

In C, il parametro setrichiede un Object[]. Non vi è alcun errore di compilazione e nessun errore di runtime quando viene chiamato con un String[]oggetto come parametro. Questo perché viene String[]lanciato Object[]. Ma l'oggetto reale ricevuto da test()rimane a String[], non è cambiato. Quindi anche l' paramsoggetto diventa a String[]. E l'elemento 0 di a String[]non può essere assegnato a a Long!

(Spero di avere tutto qui, se il mio ragionamento è sbagliato, sono sicuro che la community me lo dirà. AGGIORNATO: Ho aggiornato il codice nell'esempio A in modo che si compili effettivamente, pur mostrando ancora il punto sollevato.)


Ho provato il vostro esempio A esso è non funziona: List<Object> cannot be applied to List<String>. Non puoi passare ArrayList<String>a un metodo che ti aspetti ArrayList<Object>.
parsecer

Grazie, piuttosto tardi nella data ho modificato l'esempio A in modo che ora funzioni. Il cambiamento principale è stato definire argsList genericamente in main ().
Radfast,

4

Il problema 2 è OK, perché "System.out.println (set);" significa "System.out.println (set.toString ());" set è un'istanza di List, quindi complier chiamerà List.toString ();

public static void test(List<?> set){
set.add(new Long(2)); //--> Error  
set.add("2");    //--> Error
System.out.println(set);
} 
Element ? will not promise Long and String, so complier will  not accept Long and String Object

public static void test(List<String> set){
set.add(new Long(2)); //--> Error
set.add("2");    //--> Work
System.out.println(set);
}
Element String promise it a String, so complier will accept String Object

Problema 3: questi simboli sono uguali, ma puoi fornire loro diverse specifiche. Per esempio:

public <T extends Integer,E extends String> void p(T t, E e) {}

Problema 4: la raccolta non consente la covarianza dei parametri di tipo. Ma array consente la covarianza.


0

Hai ragione: String è un sottoinsieme di Object. Poiché String è più "preciso" di Object, è necessario eseguirne il cast per utilizzarlo come argomento per System.out.println ().

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.