uso titledborder poner crear con borde java api generics bounded-wildcard

titledborder - Java: ¿comodines delimitados o parámetro de tipo delimitado?



titledborder java (5)

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é camino debo usar? ¿El comodín es útil en este caso?


Considere seguir el ejemplo de The Java Programming by James Gosling, cuarta 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 las propias palabras del autor:

"La regla general es usar comodines cuando puedas porque el código con comodines es generalmente más legible que el código con múltiples parámetros de tipo. Cuando decidas si necesitas una variable de tipo, pregúntate si esa variable de tipo se usa para relacionar dos o más parámetros, o para relacionar un tipo de parámetro con el tipo de retorno. Si la respuesta es no, entonces 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.


Depende de lo que necesites hacer. Debe usar el parámetro de tipo delimitado 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 una List<T> shapes y una T shape , por lo tanto, podemos shapes.add(shape) segura. Si fue declarado List<? extends Shape> List<? extends Shape> , NO se puede add forma segura (porque puede tener una List<Square> y un Circle ).

Entonces, al dar un nombre a un parámetro de tipo delimitado, tenemos la opción de utilizarlo en cualquier otro lugar de nuestro método genérico. Esta información no siempre es necesaria, por supuesto, así que si no necesitas saber mucho sobre el tipo (por ejemplo, tu drawAll ), entonces solo comodín es suficiente.

Incluso si no se está refiriendo nuevamente al parámetro de tipo delimitado, aún se requiere un parámetro de tipo delimitado si tiene límites múltiples. Aquí hay una cita de las preguntas frecuentes de genéricos de Java de Angelika Langer

¿Cuál es la diferencia entre un límite de comodín y un límite de parámetro de tipo?

Un comodín puede tener solo un límite, mientras que un parámetro de tipo puede tener varios límites. Un comodín puede tener un límite inferior o superior, mientras que no existe un límite inferior para un parámetro de tipo.

Los límites de caracteres comodín y los límites de parámetros de tipo a menudo se confunden, ya que ambos se denominan límites y tienen, en parte, una sintaxis similar. [...]

Sintaxis :

type parameter bound T extends Class & Interface1 & … & InterfaceN wildcard bound upper bound ? extends SuperType lower bound ? super SubType

Un comodín puede tener solo un límite, ya sea un límite inferior o superior. No se permite una lista de límites de comodines.

Un parámetro de tipo, en constrast, puede tener varios límites, pero no existe un límite inferior para un parámetro de tipo.

Citas de Effective Java 2nd Edition, Item 28: Use comodines delimitados para aumentar la flexibilidad de la API :

Para una flexibilidad máxima, use tipos de comodines en los parámetros de entrada que representan a productores o consumidores. [...] PECS significa producer- extends , consumer- super [...]

No use tipos de comodines como tipos de devolución . En lugar de proporcionar flexibilidad adicional para sus usuarios, les obligaría a usar tipos de comodines en el código del cliente. Usados ​​correctamente, los tipos de comodines son casi invisibles para los usuarios de una clase. Causan métodos para aceptar los parámetros que deben aceptar y rechazar aquellos que deberían rechazar. Si el usuario de la clase tiene que pensar en tipos de comodines, probablemente haya algo mal con la API de la clase .

Aplicando el principio PECS, ahora podemos volver a nuestro ejemplo addIfPretty y hacerlo más flexible escribiendo lo siguiente:

public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }

Ahora podemos addIfPretty , digamos, un Circle , a una List<Object> . Esto es obviamente seguro, pero nuestra declaración original no fue lo suficientemente flexible como para permitirlo.

Preguntas relacionadas

Resumen

  • Utilice parámetros / comodines de tipo delimitados, aumentan la flexibilidad de su API
  • Si el tipo requiere varios parámetros, no tiene más remedio que usar un parámetro de tipo delimitado
  • si el tipo requiere un límite inferior, no tiene más remedio que usar un comodín delimitado
  • "Productores" tienen límites superiores, "consumidores" tienen límites inferiores
  • No use comodines en los tipos de retorno

En su ejemplo, realmente no necesita usar T, ya que no usa ese tipo en ningún otro lado.

Pero si hiciste 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, obtienes un poco más de seguridad y devuelves solo Shape, ya que puedes pasar el resultado a una función que puede tomar a un niño de Shape. Por ejemplo, puede pasar una List<Square> a mi método, y luego pasar el Cuadrado resultante a un método que solo toma Cuadrados. Si usaste ''?'' tendrías que lanzar la forma resultante a la casilla que no sería segura.

En el segundo ejemplo, se asegura de que ambas listas tengan el mismo parámetro de tipo (que no se puede hacer con ''?'', Ya que cada ''?'' Es diferente), de modo que puede crear una lista que contenga todos los elementos de ambos .


La segunda forma es un poco más detallada, pero le permite referirse a T dentro de ella:

for (T shape : shapes) { ... }

Esa es la única diferencia, por lo que yo entiendo.


Por lo que yo entiendo, el comodín permite un código más conciso en situaciones donde no se requiere un parámetro de tipo (por ejemplo, porque se hace referencia en varios lugares o porque se requieren múltiples límites como se detalla en otras respuestas).

En el enlace indica que leo (en "Métodos genéricos") las siguientes afirmaciones que sugieren en esta dirección:

Los métodos genéricos permiten que los parámetros de tipo se utilicen para expresar dependencias entre los tipos de uno o más argumentos de un método y / o su tipo de devolución. Si no existe tal dependencia, no se debe usar un método genérico.

[...]

El uso de comodines es más claro y conciso que la declaración de parámetros de tipo explícitos, por lo que debe preferirse siempre que sea posible.

[...]

Los comodines también tienen la ventaja de que pueden usarse fuera de las firmas de métodos, como los tipos de campos, variables locales y matrices.