Para su ejemplo, se puede utilizar una llanura List<Shape>
como Dan y Paul dijeron; no necesita usar la sintaxis de signo de interrogación comodín como List<? super Shape>
o List<? extends Shape>
). Creo que su pregunta subyacente podría ser "¿cuándo usaría una de las declaraciones de estilo de signo de interrogación?" (El Principio de Obtener y poner que cita Julien es una gran respuesta a esta pregunta, pero no creo que tenga mucho sentido a menos que lo vea en el contexto de un ejemplo.) Aquí está mi opinión sobre una versión ampliada del Get y Ponga Principio para cuándo usar comodines.
Use <? extends T>
si ...
- Un método tiene un parámetro genérico clase
Foo<T>
readSource
- El método obtiene instancias de T de readSource, y no le importa si el objeto actual recuperado pertenece a una subclase de T.
uso <? super T>
... si
- un método tiene un parámetro de clase genérica
Foo<T>
writeDest
- El método PONE casos de T en writeDest, y no le importa si writeDest también contiene objetos que son subclases de T.
Aquí hay un tutorial de un ejemplo concreto que ilustra la idea detrás de comodines. Imagine que está escribiendo un método processSquare que elimina un cuadrado de una lista, lo procesa y almacena el resultado en una lista de salida. Aquí está una firma de método:
void processSquare(List<Square> iSqua, List<Square> oSqua)
{ Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }
Ahora se crea una lista de DoubleSquares, que se extienden Square, y tratar de procesarlos:
List<DoubleSquare> dsqares = ...
List<Square> processed = new ArrayList<Square>;
processSquare(dsqares, processed); // compiler error! dsquares is not List<Square>
El compilador genera un error debido a que el tipo de dsquares List<DoubleSquare>
doesn 't coincide con el tipo del primer parámetro para processSquare, List<Square>
. Quizás un DoubleSquare es-un cuadrado, pero necesita decirle al compilador que un List<DoubleSquare>
es-un List<Square>
a los fines de su método processSquare. Utilice el comodín <? extends Square>
para indicar al compilador que su método puede tomar una lista de cualquier subclase de Square.
void processSquare(List<? extends Square> iSqua, List<Square> oSqua)
siguiente a mejorar la aplicación para procesar círculos, así como cuadrados. ¿Quieres agregar todas sus formas procesadas en una única lista que incluye tanto los círculos y cuadrados, por lo que ha cambiado el tipo de la lista de procesado de un List<Square>
a un List<Shape>
:
List<DoubleSquare> dsqares = ...
List<Circle> circles = ...
List<Shape> processed = new ArrayList<Square>;
processSquare(dsqares, processed); // compiler error! processed is not List<Square>
El compilador falla con un nuevo error. Ahora el tipo de la lista procesada List<Shape>
no coincide con el 2do parámetro para procesar la Plaza, List<Square>
. Utilice el comodín <? super Square>
para indicar al compilador que un parámetro dado puede ser una Lista de cualquier superclase de Square.
void processSquare(List<? extends Square> iSqua,
List<? super Square> oSqua)
Aquí está el código fuente completo para el ejemplo. A veces me resulta más fácil aprender cosas comenzando con un ejemplo de trabajo y luego descomponiéndolo para ver cómo reacciona el compilador.
package wild;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public abstract class Main {
// In processing the square,
// I'll take for input any type of List that can PRODUCE (read) squares.
// I'll take for output any type of List that can ACCEPT (write) squares.
static void processSquare(List<? extends Square> iSqua, List<? super Square> oSqua)
{ Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }
static void processCircle(List<? extends Circle> iCirc, List<? super Circle> oCirc)
{ Circle c = iCirc.remove(0); c.doCircle(); oCirc.add(c); }
public static void main(String[] args) {
// Load some inputs
List<Circle> circles = makeList(new Circle());
List<DoubleSquare> dsqares = makeList(new DoubleSquare());
// Collated storage for completed shapes
List<Shape> processed = new ArrayList<Shape>();
// Process the shapes
processSquare(dsqares, processed);
processCircle(circles, processed);
// Do post-processing
for (Shape s : processed)
s.shapeDone();
}
static class Shape { void shapeDone() { System.out.println("Done with shape."); } }
static class Square extends Shape { void doSquare() { System.out.println("Square!"); } }
static class DoubleSquare extends Square {}
static class Circle extends Shape { void doCircle() { System.out.println("Circle!"); } }
static <T> List<T> makeList(T a) {
List<T> list = new LinkedList<T>(); list.add(a); return list;
}
}
Por curiosidad, ¿cuál es el beneficio de hacer esto sobre definir una interfaz Shape y luego simplemente crear una lista? Todavía puede probar para clases específicas y lanzar si es necesario ... –
kgrad