¿Cuál es el punto del operador de diamante (<>) en Java 7?

445

El operador de diamantes en java 7 permite códigos como el siguiente:

List<String> list = new LinkedList<>();

Sin embargo, en Java 5/6, simplemente puedo escribir:

List<String> list = new LinkedList();

Según tengo entendido, el tipo de borrado es que son exactamente iguales. (El genérico se elimina en tiempo de ejecución de todos modos).

¿Por qué molestarse con el diamante? ¿Qué nueva funcionalidad / tipo de seguridad permite? Si no ofrece ninguna funcionalidad nueva, ¿por qué lo mencionan como una característica? ¿Mi comprensión de este concepto es defectuosa?

tofarr
fuente
44
Tenga en cuenta que esto no es un problema si utiliza métodos de fábrica estáticos, ya que Java escribe inferencia en las llamadas a métodos.
Brian Gordon
cuando desactivas la advertencia, es realmente inútil ... :) como para mí
Renetik
3
Se ha respondido, pero también está en el tutorial de Java (en la mitad de la página): docs.oracle.com/javase/tutorial/java/generics/…
Andreas Tasoulas
Buen artículo sobre esto en dZone .
R. Oosterholt
2
Mi opinión es que es azúcar sintáctico para List <String> list = new LinkedList <Cualquiera que sea el tipo de la izquierda es> (); es decir, mantenerlo genérico
Viktor Mellgren

Respuestas:

496

El problema con

List<String> list = new LinkedList();

es que en el lado izquierdo, está utilizando el tipo genérico, mientras List<String>que en el lado derecho está utilizando el tipo sin formato LinkedList. Los tipos sin formato en Java efectivamente solo existen para la compatibilidad con el código pre-genérico y nunca deben usarse en código nuevo a menos que sea absolutamente necesario.

Ahora, si Java tenía genéricos desde el principio y no tenía tipos, como los LinkedListque se crearon originalmente antes de tener genéricos, probablemente podría haberlo hecho para que el constructor de un tipo genérico infiera automáticamente sus parámetros de tipo desde la izquierda lado de la tarea, si es posible. Pero no lo hizo, y debe tratar los tipos sin procesar y los tipos genéricos de manera diferente para la compatibilidad con versiones anteriores. Eso hace que tengan que hacer una forma ligeramente diferente , pero igualmente conveniente, de declarar una nueva instancia de un objeto genérico sin tener que repetir sus parámetros de tipo ... el operador de diamante.

En cuanto a su ejemplo original de List<String> list = new LinkedList(), el compilador genera una advertencia para esa asignación porque debe hacerlo. Considera esto:

List<String> strings = ... // some list that contains some strings

// Totally legal since you used the raw type and lost all type checking!
List<Integer> integers = new LinkedList(strings);

Existen genéricos para proporcionar protección en tiempo de compilación contra hacer lo incorrecto. En el ejemplo anterior, el uso del tipo sin formato significa que no obtiene esta protección y obtendrá un error en tiempo de ejecución. Es por eso que no debes usar tipos sin procesar.

// Not legal since the right side is actually generic!
List<Integer> integers = new LinkedList<>(strings);

Sin embargo, el operador de diamante permite que el lado derecho de la asignación se defina como una instancia genérica verdadera con los mismos parámetros de tipo que el lado izquierdo ... sin tener que volver a escribir esos parámetros. Le permite mantener la seguridad de los genéricos con casi el mismo esfuerzo que usar el tipo sin procesar.

Creo que la clave para entender es que los tipos sin formato (sin <>) no pueden tratarse de la misma manera que los tipos genéricos. Cuando declara un tipo sin procesar, no obtiene ninguno de los beneficios y la verificación de tipos de genéricos. También debe tener en cuenta que los genéricos son una parte de propósito general del lenguaje Java ... ¡no solo se aplican a los constructores sin argumentos de Collections!

ColinD
fuente
31
La compatibilidad con versiones anteriores es excelente, pero no a expensas de la complejidad, por favor. ¿Por qué no podría Java 7 simplemente introducir el -compatibilityinterruptor del compilador mientras que si está ausente javac, prohibirá todos los tipos sin formato y solo aplicará tipos estrictamente genéricos? Esto hará que nuestros códigos sean menos detallados tal como están.
Rosdi Kasim
3
@Rosdi: De acuerdo, no hay necesidad de tipos sin formato en el nuevo código. Sin embargo, preferiría incluir el número de versión de Java en el archivo fuente (en lugar de (incorrecto) usando la línea de comando), vea mi respuesta.
maaartinus
37
Personalmente, no me gusta el uso de diamantes A MENOS QUE esté definiendo Y creando instancias en la misma línea. List<String> strings = new List<>()está bien, pero si define private List<String> my list;, y luego a la mitad de la página con la que crea una instancia my_list = new List<>(), ¡no está bien! ¿Qué contiene mi lista nuevamente? Oh, déjame buscar la definición. De repente, el beneficio del atajo de diamante se despide.
rmirabelle
11
@rmirabelle ¿Cómo es eso diferente de my_list = getTheList():? Hay varias formas mejores de lidiar con este tipo de problemas: 1. use un IDE que le muestre tipos de variables al pasar el mouse. 2. use nombres de variables más significativos, como private List<String> strings3. no divida la declaración y la inicialización de variables a menos que realmente tenga que hacerlo.
Natix
1
@ Morfidon: Sí, todavía es válido para Java 8. Estoy bastante seguro de que debería recibir advertencias.
ColinD
36

Tu comprensión es ligeramente defectuosa. El operador de diamante es una buena característica, ya que no tiene que repetirlo. Tiene sentido definir el tipo una vez cuando declara el tipo, pero simplemente no tiene sentido definirlo nuevamente en el lado derecho. El principio SECO.

Ahora para explicar toda la confusión sobre la definición de tipos. Tiene razón en que el tipo se elimina en tiempo de ejecución, pero una vez que desea recuperar algo de una Lista con definición de tipo, lo recupera como el tipo que ha definido al declarar la lista; de lo contrario, perdería todas las características específicas y solo tendría el Características del objeto, excepto cuando convierte el objeto recuperado a su tipo original, que a veces puede ser muy complicado y dar lugar a una ClassCastException.

El uso List<String> list = new LinkedList()le proporcionará advertencias de tipo crudo.

Octavian A. Damiean
fuente
8
List<String> list = new LinkedList()Es el código correcto. Tú sabes esto y yo también lo sé. Y la pregunta (según tengo entendido) es: ¿por qué solo el compilador de Java no entiende que este código es bastante seguro?
Romano
22
@Roman: noList<String> list = new LinkedList() es el código correcto. Claro, ¡sería bueno si lo fuera! Y probablemente podría haber sido si Java tuvo genéricos desde el principio y no tuvo que lidiar con la compatibilidad con versiones anteriores de tipos genéricos que solían ser no genéricos, pero lo tiene.
ColinD
66
@ColinD Java realmente no necesita lidiar con la compatibilidad con versiones anteriores en cada línea . En cualquier archivo fuente de Java que use genéricos, los tipos antiguos no genéricos deberían estar prohibidos (siempre se puede usar <?>si se conecta al código heredado) y el operador de diamante inútil no debería existir.
maaartinus
16

Esta línea provoca la advertencia [sin marcar]:

List<String> list = new LinkedList();

Entonces, la pregunta se transforma: ¿por qué la advertencia [sin marcar] no se suprime automáticamente solo para el caso cuando se crea una nueva colección?

Creo que sería una tarea mucho más difícil que agregar <>funciones.

UPD : También creo que sería un desastre si legalmente se usaran tipos sin formato "solo por algunas cosas".

romano
fuente
13

En teoría, el operador de diamante le permite escribir código más compacto (y legible) guardando argumentos de tipo repetido. En la práctica, son solo dos caracteres confusos más que no te dan nada. ¿Por qué?

  1. Ningún programador cuerdo utiliza tipos sin procesar en el nuevo código. Por lo tanto, el compilador podría simplemente suponer que al no escribir argumentos de tipo desea que los infiera.
  2. El operador de diamantes no proporciona información de tipo, solo dice el compilador, "estará bien". Entonces, al omitirlo, no puede hacer daño. En cualquier lugar donde el operador de diamantes es legal, el compilador podría "inferirlo".

En mi humilde opinión, tener una forma clara y simple de marcar una fuente como Java 7 sería más útil que inventar cosas tan extrañas. En un código tan marcado, los tipos sin procesar podrían prohibirse sin perder nada.

Por cierto, no creo que deba hacerse con un interruptor de compilación. La versión Java de un archivo de programa es un atributo del archivo, no tiene ninguna opción. Usando algo tan trivial como

package 7 com.example;

podría dejarlo claro (puede preferir algo más sofisticado que incluya una o más palabras clave elegantes). Incluso permitiría compilar fuentes escritas para diferentes versiones de Java juntas sin ningún problema. Permitiría introducir nuevas palabras clave (por ejemplo, "módulo") o eliminar algunas características obsoletas (múltiples clases no públicas no anidadas en un solo archivo o en absoluto) sin perder ninguna compatibilidad.

maaartinus
fuente
2
¿Ha considerado la diferencia entre new ArrayList(anotherList)y new ArrayList<>(anotherList)(especialmente si se le está asignando List<String>y anotherListes a List<Integer>)?
Paul Bellora
@Paul Bellora: No. Sorprendentemente para mí, ambos compilan. El que tiene diamantes, incluso sin previo aviso. Sin embargo, no veo ningún sentido en esto, ¿puedes explicarlo?
maaartinus
Lo siento, no te expliqué muy bien. Vea la diferencia entre estos dos ejemplos: ideone.com/uyHagh e ideone.com/ANkg3T Solo estoy señalando que es importante usar el operador de diamante en lugar de un tipo sin formato, al menos cuando se pasan argumentos con límites genéricos en.
Paul Bellora
En realidad no me había tomado el tiempo de leer la respuesta de ColinD: él cita casi lo mismo.
Paul Bellora
2
Entonces, si estamos a punto de introducir una nueva sintaxis para tipos sin formato, para los pocos lugares donde realmente se necesita, ¿por qué no usar algo como new @RawType List()? Esa es la sintaxis de Java 8 válida y las anotaciones de tipo permiten usarlo en todos los lugares donde sea necesario, por ejemplo @RawType List = (@RawType List) genericMethod();. Teniendo en cuenta que los tipos sin formato actualmente crean una advertencia de compilación a menos que @SuppressWarningsse haya colocado un apropiado , @RawTypesería un reemplazo razonable y no se necesita una sintaxis más sutil.
Holger
8

Cuando escribe List<String> list = new LinkedList();, el compilador genera una advertencia "sin marcar". Puede ignorarlo, pero si solía ignorar estas advertencias, también puede pasar por alto una advertencia que le notifica sobre un problema de seguridad de tipo real.

Por lo tanto, es mejor escribir un código que no genere advertencias adicionales, y el operador de diamante le permite hacerlo de manera conveniente sin repeticiones innecesarias.

axtavt
fuente
4

Todo lo dicho en las otras respuestas son válidas, pero los casos de uso no son completamente válidos en mi humilde opinión. Si se comprueba la guayaba y especialmente las cosas relacionadas con las colecciones, lo mismo se ha hecho con métodos estáticos. Por ejemplo, Lists.newArrayList () que te permite escribir

List<String> names = Lists.newArrayList();

o con importación estática

import static com.google.common.collect.Lists.*;
...
List<String> names = newArrayList();
List<String> names = newArrayList("one", "two", "three");

La guayaba tiene otras características muy poderosas como esta y en realidad no puedo pensar en muchos usos para el <>.

Hubiera sido más útil si optaran por hacer que el comportamiento del operador de diamante sea el predeterminado, es decir, el tipo se deduce del lado izquierdo de la expresión o si el tipo del lado izquierdo se deduce del lado derecho. Esto último es lo que sucede en Scala.

allprog
fuente
3

El objetivo para el operador de diamantes es simplemente reducir la tipificación de código al declarar tipos genéricos. No tiene ningún efecto sobre el tiempo de ejecución.

La única diferencia si especifica en Java 5 y 6,

List<String> list = new ArrayList();

es que usted tiene que especificar @SuppressWarnings("unchecked")la list(de lo contrario obtendrá una advertencia fundido sin marcar). Tengo entendido que el operador de diamantes está tratando de facilitar el desarrollo. No tiene nada que ver con la ejecución de genéricos en tiempo de ejecución.

Buhake Sindi
fuente
ni siquiera tiene que usar esa anotación. Al menos en Eclipse, puedes decirle al compilador que no te advierta sobre esto y estás bien ...
Xerus
Es mejor tener la anotación. No todos los desarrolladores usan Eclipse aquí.
Buhake Sindi