2011-08-28 11 views
13

Si tengo una clase Foo:inferencia Genérico en los constructores

public class Foo<T> { 
    public Foo(T t) { 
     //do something 
    } 

    public static <E> void bar(E e) { 
     //do something 
    } 
} 

¿Por qué Foo.bar("String"); inferir que E es una cadena (y por lo tanto no lanzar una advertencia del compilador), pero no new Foo("String"); inferir que T es una cadena?

+1

¿Cuál es el error exacto que está viendo? – linuxuser27

+0

@ linuxuser27 Obtiene una advertencia de tipo sin formato en 'nuevo Foo (" Cadena ");' – Jeffrey

Respuesta

10

Como el constructor se puede considerar como un método de instancia especial, no se escribe - obtiene su tipo del nombre de clase (con un parámetro de tipo), por ejemplo Foo<String>. es decir, el constructor no se define como:

public <T> Foo(T t) ... 

ni puede ser. Si lo hace, podría ocultar el tipo genérico de la clase (y obtendrá una advertencia)

El método estático sin embargo es mecanografiado. Para su información, la llamada genérica, sin parámetros, una vez que se infiere del tipo, es equivalente a:

Foo.<String>bar("String"); 
+1

+1. Muy interesante, pero todavía no veo por qué en este caso no se puede inferir por un mecanismo similar de la lista de argumentos – linuxuser27

+0

¿Ha notado esto: si declara su clase así: 'clase TestClass ' y su constructor 'public TestClass (T t)' no recibe una advertencia, pero si lo hace: 'clase TestClass ' y su constructor 'público TestClass (T t)' recibe una advertencia del compilador que indica "El parámetro tipo T está ocultando el tipo T " –

+0

@Varun Achar Puede declarar [constructores genéricos] (http://download.oracle.com/javase/tutorial/java/generics/genmethods.html), y si vuelve a declarar en un constructor de hecho, está ocultando la clase '. Creo que Boehemian está tratando de decir que dado que no estoy creando un constructor genérico, está heredando su tipo de la clase, no al revés. Pero todavía deja una pregunta de por qué la clase no puede inferir el parámetro de tipo de un constructor. – Jeffrey

1

creo que necesita para hacer esto

new Foo<String>("String"); 

a decir obtener la información de los genéricos pasado; similar a la API de Colecciones.

+2

Eso es correcto. El OP no está preguntando "qué" debe hacerse sino "por qué", ya que en el caso de una llamada a un método estático esto no es necesario. – linuxuser27

+0

Lo que dijo linuxuser, al hacer 'nuevo Foo (" String ");' parece repetitivo cuando ya está pasando un 'String' al constructor. – Jeffrey

+0

Creo que va a ser menos repetitivo en Java 7. –

1

Buscando en esto, voy a tomar una conjetura aquí. Considere lo siguiente:

public class Foo { 
    public <E> Foo(E t) { 
     //do something 
    } 

    public static <E> void bar(E e) { 
     //do something 
    } 
} 

En la clase anterior se obtiene ninguna advertencia cuando una instancia de Foo de la siguiente manera:

Foo f = new Foo("String"); 

Esto funciona porque el tipo de E se infiere aquí. Tal como espera que ocurra en el caso del método. Sin embargo, el error que está obteniendo no se debe a que el tipo de argumento no se esté inferiendo, sino porque no se puede inferir el tipo sin procesar para la clase.

Creo que esto se debe a que el tipo de clase sin formato se puede propagar a los métodos, pero los métodos no pueden establecer el tipo de clase sin procesar mediante la inferencia.

+0

Creo que esto es a lo que Bohemian estaba llegando. Como no declaras el parámetro de tipo de clase, al constructor no se le asigna un tipo. – Jeffrey

+0

@Jeffrey - Estoy de acuerdo.Esta fue una gran pregunta. Gracias. – linuxuser27

3

Cuando Java implementó genéricos, se decidió que una clase genérica instanciada sin parámetros de tipo siempre devolvería un tipo sin formato. Esto difiere de los métodos genéricos que faltan parámetros de tipo, que el compilador intenta inferir el tipo de. De los tutoriales de Java:

En general, el compilador de Java puede inferir los parámetros de tipo de una llamada al método genérico. En consecuencia, en la mayoría de los casos, no es necesario que los especifique.

Pero cuando la discusión se dirige a los constructores:

Tenga en cuenta que para tomar ventaja de la inferencia de tipo automático durante la instanciación de la clase genérica, debe especificar el diamante.En el siguiente ejemplo, el compilador genera una advertencia de conversión sin control debido a que el HashMap() constructor se refiere al tipo prima HashMap, no el tipo Map<String, List<String>>:

Map<String, List<String>> myMap = new HashMap(); // unchecked conversion warning

fuente: http://download.oracle.com/javase/tutorial/java/generics/gentypeinference.html

Esto sigue siendo lo mismo en Java 7, sin embargo, trataron de hacerlo menos repetitivo al admitir la sintaxis del diamante. Por ejemplo, Foo<String> foo = new Foo<>("String"). Ver this section of the same article.

+0

Buena documentación. – Jeffrey

1

@Kublai Khan tiene la respuesta correcta; el tipo de new Foo(s) es raw Foo, para compatibilidad con versiones anteriores.

La inferencia del tipo de diamante del constructor de Java7 (new Foo<>(s)) es la misma, y ​​se define en términos de, la inferencia del tipo de método en el método.

http://cr.openjdk.java.net/~darcy/ProjectCoin/ProjectCoin-Documentation-v0.9375.html#diamond

Si la expresión creación de la instancia de clase utiliza "<>" para eludir argumentos de tipo de clase, una lista de métodos m1 ... mk se definen a los efectos de la resolución de sobrecarga y la inferencia tipo de argumento. ..

... entonces uno de m1 ... mk se selecciona, utilizando el proceso descrito en §15.12.2 (Determinar Método Signature)

Su sospecha es correcta, constructores c una inferencia de uso al igual que los métodos, no hay diferencias esenciales. Solo tiene que agregar <> debido a un problema de compatibilidad con versiones anteriores.

Por qué Java tardó 6 años en agregar esta función es otra historia.

Cuestiones relacionadas