2011-02-10 16 views
7

Sto avendo un crampo al cervello: Ho un public interface SomeInterface e un static private class SomeClass e sto cercando di restituire un List<SomeInterface> da uno dei miei metodi, ma ho la errore (sulla linea return list; sottostante):java: conversione da Lista <SomeClass> la lista <SomeInterface> quando SomeClass implementa SomeInterface

Type mismatch: cannot convert from List<GenericList1.SomeClass> to 
List<GenericList1.SomeInterface> 

Come posso risolvere questo problema senza dover creare una nuova lista?

Precisazione: Non voglio l'elenco per essere creato come List<SomeInterface> (forse la soluzione più ovvia) perché privatamente voglio mantenere un List<SomeClass> per consentire l'accesso futuro ai metodi di SomeClass oltre a quelli nell'interfaccia pubblica. Questo non è mostrato nel caso di esempio qui sotto, ma nel mio vero programma ho bisogno di questo.

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

public class GenericList1 { 

    public interface SomeInterface 
    { 
     public int getX(); 
    } 

    private static class SomeClass implements SomeInterface 
    { 
     final private int x; 
     @Override public int getX() { 
      return this.x; 
     }  
     public SomeClass(int x) { this.x = x; } 
    } 

    public static void main(String[] args) { 
     List<SomeInterface> list = createList(10); 
     printList(list); 
    } 

    private static void printList(List<SomeInterface> list) { 
     for (SomeInterface si : list) 
      System.out.println(si.getX()); 
    } 

    private static List<SomeInterface> createList(int n) { 
     List<SomeClass> list = new ArrayList<SomeClass>(); 
     for (int i = 0; i < n; ++i) 
      list.add(new SomeClass(i)); 
     return list; 
    } 
} 

risposta

23

È necessario ridefinire il metodo di come

private static List<? extends SomeInterface> createList(int n) { ... 

e allo stesso modo, le altre dichiarazioni della lista. Ciò consente di trattare le liste generiche in modo polimorfico, , purché si leggano solo i valori da esse.

(Se volete aggiungere nuovi elementi a un elenco, si dovrebbe usare List<? super SomeInterface> invece.)

Questo idioma è conosciuto con la sigla PECS-Produttore: Estende/consumatori: Super, coniato da Josh Bloch Effective Java in 2a edizione, punto 28.

Come altri hanno notato, questo idioma è necessaria perché un List<SomeClass> è non un List<SomeInterface>, anche quando SomeClass implements SomeInterface. La ragione di ciò è spiegata dettagliatamente nel documento di riferimento.

+0

ah, OK, questo funziona. Nel mio programma reale sto restituendo una lista <> incapsulata da Collections.unmodifiableList() in modo che possano essere letti solo. –

+0

La risposta aveva un collegamento all'articolo 28 ma il collegamento è ora morto. Un PDF del libro è disponibile qui: https://github.com/ldfaiztt/CSE331/blob/master/Effective.Java.2nd.Edition.May.2008.3000th.Release.pdf – Paul

6

Contrariamente alla credenza popolare, List<Derived> non ha assolutamente alcuna relazione con List<Base>, anche se Derived extends Base.

Solo perché le classi condividono una gerarchia non significa che le raccolte di esse facciano. Questo è vero per i generici in generale.

Si potrebbe modificare il vostro codice come segue:

private static List<SomeInterface> createList(int n) { 
     List<SomeInterface> list = new ArrayList<SomeInterface>(); 
     for (int i = 0; i < n; ++i) 
      list.add(new SomeClass(i)); //add a SomeClass object - valid, because it has the interface 
    return list; 
} 
+0

sì, questa è la soluzione più ovvia, ma io non voglio farlo; guarda le mie modifiche. –

0
private static List<SomeInterface> createList(int n) { 
    List<SomeInterface> list = new ArrayList<SomeInterface>(); 
    for (int i = 0; i < n; ++i) 
     list.add(new SomeClass(i)); 
    return list; 
} 
+0

questa è la soluzione più ovvia ma non voglio farlo; guarda le mie modifiche. –

1

vorrei cambiare le firme dei metodi in questo modo:

private static void printList(List<? extends SomeInterface> list) { 
    for (SomeInterface si : list) 
     System.out.println(si.getX()); 
} 

private static <T super SomeClass> List<T> createList(int n) { 
    List<T> list = new ArrayList<T>(); 
    for (int i = 0; i < n; ++i) 
     list.add(new SomeClass(i)); 
    return list; 
} 

Utilizzo di un jolly in un tipo di ritorno è spesso una cattiva idea, perché ti costringe a usare caratteri jolly in tutti i metodi di chiamata. Ho creato createList un metodo generico in modo che il tipo restituito possa essere il più flessibile possibile pur rimanendo il più specifico possibile.

Problemi correlati