java generics syntax constructor generic-type-argument

java - ¿Qué significan los argumentos de tipo constructor cuando se colocan*antes de*el tipo?



generics syntax (2)

Recientemente he encontrado esta inusual (para mí) sintaxis de Java ... aquí hay un ejemplo de ello:

List list = new <String, Long>ArrayList();

Observe la posición de los argumentos de tipo <String, Long> ... no es posterior al tipo normal sino antes. No me importa admitir que nunca he visto esta sintaxis antes. También tenga en cuenta que hay 2 argumentos de tipo cuando ArrayList solo tiene 1.

¿El posicionamiento de los argumentos de tipo tiene el mismo significado que ponerlos después del tipo? Si no, ¿qué significa el posicionamiento diferente?

¿Por qué es legal tener 2 argumentos de tipo cuando ArrayList solo tiene 1?

He buscado los lugares habituales, por ejemplo. Angelika Langer y aquí, pero no pueden encontrar ninguna mención de esta sintaxis en ninguna parte, aparte de las reglas gramaticales en el archivo de gramática Java en el proyecto ANTLR.


Llamando a un constructor genérico

Esto es inusual bien, pero Java totalmente válido. Para comprender, necesitamos saber que una clase puede tener un constructor genérico, por ejemplo:

public class TypeWithGenericConstructor { public <T> TypeWithGenericConstructor(T arg) { // TODO Auto-generated constructor stub } }

Supongo que la mayoría de las veces, al crear una instancia de la clase a través del constructor genérico, no necesitamos hacer explícito el argumento de tipo. Por ejemplo:

new TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));

Ahora T es claramente LocalDate . Sin embargo, puede haber casos en los que Java no pueda inferir (deducir) el argumento de tipo. Luego lo suministramos explícitamente usando la sintaxis de su pregunta:

new <LocalDate>TypeWithGenericConstructor(null);

Por supuesto, también podemos suministrarlo, aunque no sea necesario si creemos que ayuda a la legibilidad o por cualquier motivo:

new <LocalDate>TypeWithGenericConstructor(LocalDate.now(ZoneId.systemDefault()));

En tu pregunta, parece que estás llamando al constructor java.util.ArrayList . Ese constructor no es genérico (solo la clase ArrayList es, eso es otra cosa). Por qué Java le permite proporcionar argumentos de tipo en la llamada cuando no se utilizan, consulte mi edición a continuación. Mi Eclipse me da una advertencia :

Argumentos de tipo no utilizados para el constructor no genérico ArrayList () de tipo ArrayList; No debe ser parametrizado con argumentos.

Pero no es un error, y el programa funciona bien (también recibo advertencias sobre los argumentos de tipo faltantes para List y ArrayList , pero eso es otra historia).

Clase genérica versus constructor genérico

¿El posicionamiento de los argumentos de tipo tiene el mismo significado que ponerlos después del tipo? Si no, ¿qué significa el posicionamiento diferente?

No, es diferente. El tipo habitual de argumento / s después del tipo ( ArrayList<Integer>() ) es para la clase genérica . Los argumentos de tipo anteriores son para el constructor .

Las dos formas también se pueden combinar:

List<Integer> list = new <String, Long>ArrayList<Integer>();

Considero que esto es un poco más correcto, ya que ahora podemos ver que la lista almacena objetos Integer (por supuesto, prefiero dejar de lado el significado <String, Long> ).

¿Por qué es legal tener 2 argumentos de tipo cuando ArrayList solo tiene 1?

Primero, si suministra argumentos de tipo antes del tipo, debe proporcionar el número correcto para el constructor, no para la clase, por lo que no tiene nada que ver con cuántos argumentos de tipo tiene la clase ArrayList . Eso realmente significa que, en este caso, no debe proporcionar ninguno ya que el constructor no toma argumentos de tipo (no es genérico). Cuando suministra algunos de todos modos, se ignoran, por lo que no importa cuántos o cuántos suministre.

¿Por qué se permiten argumentos de tipo sin sentido?

Edite con agradecimiento a @Slaw por el enlace: Java permite argumentos de tipo en todas las llamadas a métodos. Si el método llamado es genérico, se usan los argumentos de tipo; Si no, son ignorados. Por ejemplo:

int length = "My string".<List>length();

Sí, es absurdo. La especificación del lenguaje Java (JLS) proporciona esta justificación en la subsección 15.12.2.1:

Esta regla se deriva de cuestiones de compatibilidad y principios de sustituibilidad. Como las interfaces o las superclases pueden generarse independientemente de sus subtipos, podemos anular un método genérico con uno no genérico. Sin embargo, el método de anulación (no genérico) debe ser aplicable a las llamadas al método genérico, incluidas las llamadas que pasan explícitamente los argumentos de tipo. De lo contrario, el subtipo no sería sustituible por su supertipo generado.

El argumento no es válido para los constructores, ya que no se pueden anular directamente. Pero supongo que querían tener la misma regla para no complicar demasiado las reglas ya complicadas. En cualquier caso, la sección 15.9.3 sobre creación de instancias y new más de una vez se refiere a 15.12.2.

Campo de golf


Aparentemente, puede prefijar cualquier método / constructor no genérico con cualquier parámetro genérico que desee:

new <Long>String(); Thread.currentThread().<Long>getName();

Al compilador no le importa, porque no tiene que hacer coincidir estos argumentos de tipo con parámetros genéricos reales.

Tan pronto como el compilador tiene que verificar los argumentos, se queja de una falta de coincidencia:

Collections.<String, Long>singleton("A"); // does not compile

Me parece un error de compilación.