2009-09-03 22 views
7

E.G. para crear un ArrayList de Strings tenemos que hacer algo como¿Por qué Java no es compatible con la inferencia de tipos para los constructores?

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

mientras que debería ser capaz de inferir el tipo de parámetro para el constructor de modo que sólo tenemos que escribir

List<String> list = new ArrayList(); 

Por qué no puede el tipo se ingiere de la misma manera que los parámetros de tipo se introducen para métodos genéricos.

+4

¿Existe un propósito al preguntar "¿por qué el lenguaje X no tiene Y"? La respuesta suele ser "porque no fue diseñada para". – skaffman

+1

¡Por qué! ¡Oh por qué! Esa es una de mis mayores quejas sobre la implementación de genéricos en Java. Envolver el constructor en un método de fábrica estático hace que la inferencia de tipo funcione como un amuleto, ¿por qué no con el constructor? –

+0

@skaffman: cierto, pero lo confuso es que la inferencia de tipo existe para los métodos parametrizados, por lo que las reglas y la complejidad de la implementación ya existen. ¡Simplemente no para la instanciación de objetos! –

Respuesta

22

Esta es una mejora de Java 7 también conocida como el operador de diamantes. La sintaxis será:

List<String> strings = new ArrayList<>(); 

la propuesta oficial, accepted for inclusion in Project Coin es Improved Type Inference for Generic Instance Creation

Según Rémi Forax, el cambio ya está en el repositorio mercurial.

+0

Si bien es bueno saberlo, no responde la pregunta de por qué la versión actual * no * admite esto. –

+0

@Joachim Sauer: No puedo ofrecer una respuesta definitiva sobre por qué, pero al leer los informes de errores referidos por la propuesta de Diamond, puedo ver que la solución no se entendía bien en el marco de tiempo de Java 5. Diablos, los genéricos están un poco trabajados incluso sin presionar la inferencia del constructor en Java 5. –

+3

@Joachim ... Creo que la frase que está buscando es "supervisión del diseño". –

2

Puede ser y es una característica programada para su inclusión en JDK7.

1

Se puede inferir el tipo, pero los autores simplemente decidieron que es mejor no tener ninguna inferencia de tipo y luego tener alguna inferencia de tipo limitada en algunos casos.

Si desea realizar inferencias sobre jvm, consulte scala.

+1

¡El problema es que hay alguna inferencia tipo! Los parámetros de tipo en los métodos parametrizados se transmiten ¡muy bien! ¿Por qué no se pueden aplicar las mismas reglas a la creación de objetos? –

6

Como han dicho otros, esto es on the list for Java 7. Sin embargo, me gustaría señalar que el Google Collections Library ya es compatible con esto para varias colecciones, si está contento de usar importaciones estáticas. Por ejemplo, con frecuencia escribir código como:

List<String> list = newArrayList(); 

Todo lo que necesita es la importación estática y quitar el espacio entre new y ArrayList() :)

(La importación estática sería la de la clase Lists, por cierto . Hay métodos similares para mapas, etc.)

+4

Personalmente creo que esto es muy feo. :) – Bombe

+4

Es extraño cuando lo ves por primera vez, pero cuando estás acostumbrado, puede reducir significativamente el desorden. –

+0

+1. Lo uso sin pensar en estos días, porque Eclipse insertará la importación cuando se autocomplete, y tengo Google Collections en mi ruta de compilación predeterminada. – finnw

2

Aparte de características JDK7, supongo que lo que puede utilizar extiende y súper.

class Animal {} 
class Dog extends Animal {} 

List<? extends Animal> anims = new ArrayList<Dog>(); 
List<? super Dog> superdogs = new ArrayList<Animal>(); 

No podría inferir en estos dos casos.

+0

La inferencia de tipo solo se usaría si no se especificara manualmente (al igual que con los métodos tipados en este momento), por lo que no sería imposible. –

2

Quiere una razón por la cual Java actual no lo admite.

Solo puedo decir que Java suele dar pequeños pasos siempre que sea posible. Supongo que hubo un pequeño problema técnico que no estaban seguros de obtener "Correcto" antes de Java 7, probablemente algo relacionado con estar absolutamente seguros de que no crearía un caso ambiguo en algún código anterior o no genérico. .

Note como Robert señaló que la sintaxis será la siguiente:

List<String> strings = new ArrayList<>(); 

Aviso al vacío <>? Supongo que es para eliminar la ambigüedad de este tipo de inferencia a partir del código no genérico anterior; probablemente sea algo en lo que no pensaron al principio.

+0

Sí, acabo de leer los enlaces de Robert y el <> es para eliminar la ambigüedad de los tipos crudos que dices. – Tarski

+0

Interesante que este tipo de dirección opuesta tomada en C# donde el tipo explícito está en el lado derecho y el tipo de la variable en su lugar se deduce. Tenga en cuenta que en la implementación de C# esto también le permite inferir tipos de retorno de otras llamadas a métodos sin tener que definir explícitamente el tipo. var strings = new ArrayList (); Debo añadir que al principio no me interesaba demasiado la palabra clave var en C#, pero cuanto más puedo usarla y junto con la inferencia tipo de genéricos y tipos anónimos, todo se vuelve muy cohesivo. – jpierson

0

No soy un súper experto en Java, por lo que no estoy completamente seguro de lo que voy a decir. Aquí están mis pensamientos:

Como Java implementa genéricos por borrado, para cada tipo genérico hay un tipo sin procesar subyacente. Si define un tipo genérico, habrá un tipo sin procesar subyacente que usará Object por todas partes.

Cuando se instancia un nuevo ArrayList, sería un error de compilador para inferir el parámetro de tipo de la clase de instancias (ArrayList<String> en su ejemplo), ya que hay una clase con ese nombre exacto y ningún parámetro de tipo (que es la tipo sin procesar, ArrayList). También creo que esta es la razón por la cual en java 7 tendrá que agregar <> a la llamada del constructor para decirle al compilador que deduzca el tipo.

Se podría argumentar que el compilador debe crear una instancia del tipo sin formato solo cuando la clase de definición es el tipo sin formato, pero creo que sería confuso. Creo que el compilador debe inferir a partir de expresiones incompletas que no serían válidas sin un contexto dado, que no es el caso para la declaración new ArrayList().

Espero que esto esté claro, y que si me equivoco alguien me puede corregir.


Nota al margen:

Además, ten en cuenta que la clase prima no es el mismo que el tipo usando Object como parámetro de tipo:

List<String> list = new ArrayList(); 

es válida, en tanto que

List<String> list = new ArrayList<Object>(); 

no lo es. En el primer caso, el tipo sin procesar se puede usar como si fuera un tipo genérico, pero en el segundo caso se pregunta por la contravariación que no está disponible (no si no se usan comodines).

+0

Me dio ganas de responder a esta pregunta porque creo que ninguna de las respuestas (que son todas excelentes) aquí aborda realmente la pregunta inicial que es * why * :) – Philippe

Cuestiones relacionadas