Recientemente, leí este artículo: http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html
Mi pregunta es, en lugar de crear un método como este:
public void drawAll(List<? extends Shape> shapes){
for (Shape s: shapes) {
s.draw(this);
}
}
Puedo crear un método como este y funciona bien:
public <T extends Shape> void drawAll(List<T> shapes){
for (Shape s: shapes) {
s.draw(this);
}
}
¿Qué forma debo usar? ¿Es útil el comodín en este caso?
java
api
generics
bounded-wildcard
Tony Le
fuente
fuente
Respuestas:
Depende de lo que necesites hacer. Debe utilizar el parámetro de tipo acotado si desea hacer algo como esto:
public <T extends Shape> void addIfPretty(List<T> shapes, T shape) { if (shape.isPretty()) { shapes.add(shape); } }
Aquí tenemos un
List<T> shapes
y unT shape
, por lo tanto, podemos hacerlo con seguridadshapes.add(shape)
. Si fue declaradoList<? extends Shape>
, NO puedeadd
hacerlo con seguridad (porque puede tener unList<Square>
y unCircle
).Entonces, al darle un nombre a un parámetro de tipo acotado, tenemos la opción de usarlo en otro lugar de nuestro método genérico. Esta información no siempre es necesaria, por supuesto, por lo que si no necesita saber mucho sobre el tipo (por ejemplo, su
drawAll
), entonces basta con utilizar comodines.Incluso si no se está refiriendo al parámetro de tipo delimitado nuevamente, se requiere un parámetro de tipo delimitado si tiene varios límites. Aquí hay una cita de las preguntas frecuentes sobre genéricos de Java de Angelika Langer
Citas de Effective Java 2nd Edition, artículo 28: Utilice comodines delimitados para aumentar la flexibilidad de la API :
Aplicando el principio PECS, ahora podemos volver a nuestro
addIfPretty
ejemplo y hacerlo más flexible escribiendo lo siguiente:public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }
Ahora podemos
addIfPretty
, decir, aCircle
, a aList<Object>
. Obviamente, esto es seguro para los tipos y, sin embargo, nuestra declaración original no era lo suficientemente flexible como para permitirlo.Preguntas relacionadas
<? super T>
significa y cuándo debe usarse y cómo esta construcción debe cooperar con<T>
y<? extends T>
?Resumen
fuente
reverse(List<?>)
ejemplo de JLS java.sun.com/docs/books/jls/third_edition/html/…?
como un parámetro de métododoWork(? type)
porque entonces no puede usar ese parámetro en el método. Necesita usar el parámetro de tipo.En su ejemplo, realmente no necesita usar T, ya que no usa ese tipo en ningún otro lugar.
Pero si hicieras algo como:
public <T extends Shape> T drawFirstAndReturnIt(List<T> shapes){ T s = shapes.get(0); s.draw(this); return s; }
o como dijo polygenlubricants, si desea hacer coincidir el parámetro de tipo en la lista con otro parámetro de tipo:
public <T extends Shape> void mergeThenDraw(List<T> shapes1, List<T> shapes2) { List<T> mergedList = new ArrayList<T>(); mergedList.addAll(shapes1); mergedList.addAll(shapes2); for (Shape s: mergedList) { s.draw(this); } }
En el primer ejemplo, obtiene un poco más de seguridad de tipos y luego devuelve solo Shape, ya que luego puede pasar el resultado a una función que puede tomar un hijo de Shape. Por ejemplo, puede pasar un
List<Square>
a mi método y luego pasar el Cuadrado resultante a un método que solo toma Cuadrados. Si usó '?' tendría que convertir la forma resultante en cuadrado, lo que no sería seguro para los tipos.En el segundo ejemplo, se asegura de que ambas listas tengan el mismo parámetro de tipo (lo que no puede hacer con '?', Ya que cada '?' Es diferente), de modo que pueda crear una lista que contenga todos los elementos de ambos .
fuente
Shape s = ...
debería serT s = ...
, de lo contrarioreturn s;
no debería compilar.Considere el siguiente ejemplo de The Java Programming por James Gosling 4ta edición a continuación donde queremos fusionar 2 SinglyLinkQueue:
public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){ // merge s element into d } public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){ // merge s element into d }
Ambos métodos anteriores tienen la misma funcionalidad. Entonces, ¿cuál es preferible? La respuesta es la segunda. En palabras del propio autor:
"La regla general es usar comodines cuando sea posible porque el código con comodines es generalmente más legible que el código con varios parámetros de tipo. Cuando decida si necesita una variable de tipo, pregúntese si esa variable de tipo se usa para relacionar dos o más parámetros. o relacionar un tipo de parámetro con el tipo de retorno. Si la respuesta es no, un comodín debería ser suficiente ".
Nota: En el libro, solo se proporciona el segundo método y el nombre del parámetro de tipo es S en lugar de 'T'. El primer método no está en el libro.
fuente
Por lo que tengo entendido, el comodín permite un código más conciso en situaciones en las que no se requiere un parámetro de tipo (por ejemplo, porque se hace referencia a él en varios lugares o porque se requieren múltiples límites como se detalla en otras respuestas).
En el enlace indicas que leo (bajo "Métodos genéricos") las siguientes declaraciones que apuntan en esta dirección:
fuente
La segunda forma es un poco más detallada, pero te permite hacer referencia a
T
ella:for (T shape : shapes) { ... }
Esa es la única diferencia, hasta donde tengo entendido.
fuente