2010-02-10 10 views
5

Encontré un código hoy que encontré cuestionable. Aquí hay un ejemplo simplificado (no realista).Uso de genéricos de Java en interfaces que devuelven colecciones. ¿Mejores prácticas? Trampas?

public interface IListable { 
    //returns first n items from list 
    public ArrayList getFirstNThings(int n); 

    //returns last n items from list 
    public ArrayList getLastNThings(int n); 
} 

Entonces hay un implementador de este modo:

public GroceryList implements IListable { 
    private ArrayList<GroceryItem> groceries; 

    public GroceryList() { 
     this.groceries = new ArrayList<GroceryItem>(); 
    } 

    public ArrayList<GroceryItem> getFirstNThings(int n) { 
     ArrayList<GroceryItem> firstNThings = new ArrayList<GroceryItem>(); 
     for (int i=0; i < n; i++) { 
      firstNThings.add(this.groceries.get(i)); 
     } 
     return firstNThings 
    } 

    public ArrayList<GroceryItem> getLastNThings(int n) { 
     ArrayList<GroceryItem> lastNThings = new ArrayList<GroceryItem>(); 
     for (int i=this.groceries.size(); i < this.groceries.size()-n; i--) { 
      lastNThings.add(this.groceries.get(i-1); 
     } 
     return lastNThings; 
     } 
} 

ignorar cualquier problemas de implementación que puede encontrar en el que (me encontré con unos pocos también). Lo que quiero decir es que la interfaz no utiliza ningún parámetro de tipo genérico para ArrayList (es decir, ArrayList <?>), Pero el implementador del método de la interfaz sí lo hace (es decir, ArrayList < GroceryList>). Otros implementadores pueden devolver ArrayLists con cualquier otro tipo de parámetros, ¿no?

Entonces mis preguntas: ¿Esto es un problema? ¿Debería refacturar algo? ¿Vale la pena? ¿Cuál es la ventaja? ¿Con qué tipo de problemas puedo encontrarme si tengo un método definido en una interfaz cuyo tipo de devolución es un tipo sin procesar, pero los implementadores reales del método devuelven varios tipos parametrizados?

+2

Además de lo que dijo sfussenegger, también preferiría que los métodos devuelvan 'List' en lugar de' ArrayList'. –

Respuesta

5

si ambos métodos de IListable siempre devuelven el mismo tipo, utilice en su lugar:

public interface IListable<T> { 
    //returns first n items from list 
    public ArrayList<T> getFirstNThings(int n); 

    //returns last n items from list 
    public ArrayList<T> getLastNThings(int n); 
} 

si esto no es una opción, trate de usar? en lugar. Si bien es básicamente lo mismo, evita advertencias desagradables.

public interface IListable { 
    //returns first n items from list 
    public ArrayList<?> getFirstNThings(int n); 

    //returns last n items from list 
    public ArrayList<?> getLastNThings(int n); 
} 

En general, no es un problema para utilizar un tipo de retorno más específica en una aplicación que en un super-tipo o interfaz. Si está tratando con IListable, necesita manejar cualquier tipo de objeto en la lista devuelta. Si está tratando con GroceryList, solo espera GroceryItems. Eso no solo es cierto para los argumentos de tipo genérico de los tipos de retorno, sino también para el tipo de retorno mismo. Entonces, si una interfaz especifica List<Foo> get(), está bien implementarla como ArrayList<Foo> get().

+0

Este método también le permite convertir GroceryList en una lista tipeada totalmente genérica para volver a utilizarla más adelante. – Thirler

0

La mejor práctica es no volver nunca un List<?> con pura comodín en su código abierto, al igual que usted no debe volver null.

Los genéricos comodines en Java contaminarán a todos los codificadores utilizando su código. Puede hacerlo en su código Close para resolver un problema local.

Por lo general, se evitará volver covarianza y contravarianza comodines como List<? extends User> y List<? super User>

Puede hacerlo si sabe lo que está haciendo y leer todo sobre PECS y bounded wildcards. No lo haga solo porque compila.