2012-03-13 17 views
9

que tienen estas dos interfaces y clases:parámetros genéricos redundantes

public interface Identifiable<T> { 
    T getId(); 
} 

public interface GenericRepository<T extends Identifiable<K>, K> { 
    T get(K id); 
} 

public class MyEntity implements Identifiable<Long> { 

    private Long id; 

    public Long getId() { 
     return id; 
    } 
} 

public class MyService { 
    private GenericRepository<MyEntity, Long> myEntityRepository; 
} 

todo funciona como se desea. Pero, en mi opinión, el segundo parámetro genérico en GenericRepository (K) es redundante. Porque sé que es un myEntity identificable, creo que sería muy bueno si por fin puedo utilizar de esta manera:

public class MyService { 
    private GenericRepository<MyEntity> myEntityRepository; 
} 

Pero estoy tratando cosas diferentes sin tener éxito. ¿Es posible? ¿Si no, porque no?

ACTUALIZACIÓN: Respondiendo algunas respuestas. Creo que el compilador sabe algo sobre qué tipo es el genérico en MyEntity. Por ejemplo:

public class MyEntityGenericRepository implements GenericRepository<MyEntity, Long> { 
    // compiles... 
} 

public class MyEntityGenericRepository implements GenericRepository<MyEntity, String> { 
    // compiler says: "Bound mismatch: The type MyEntity is not a valid substitute for the bounded parameter <T extends Identifiable<K>> of the type GenericRepository<T,K>" 
} 

Respuesta

7

No creo que pueda omitirlo. Con T extends Identifiable<K>, quiere decir que el parámetro de tipo genérico debe ser Identifiable. Desde Identifiable es una clase genérica, es necesario mencionar su parámetro de tipo genérico también (si desea jugar con las reglas que es - si se omite, se pierde toda la seguridad de tipo genérico para GenericRepository, debido a las normas de compatibilidad hacia atrás). Tenga en cuenta también que K se usa realmente como el tipo de parámetro de GenericRepository.get. Y dado que ese tipo puede ser diferente de T, debe cumplir el compilador al declararlo como otro parámetro de tipo genérico de GenericRepository. De lo contrario, el compilador no tiene forma de saber qué es K.

+0

Gracias, pero no estoy de acuerdo . Por favor, mira mi edición. – sinuhepop

+3

@sinuhepop, por supuesto, el compilador puede verificar que el tipo real de 'K' coincida con el del parámetro de tipo en' Identificable '. Aún así, necesita * declarar * K como un parámetro de tipo genérico (y, en consecuencia, pasar su valor real en el momento de la instanciación). Así es como funciona el lenguaje. Tienes razón de que este conocimiento * podría * en teoría ser inferido por el compilador, pero no es por ahora, y por muchos años aún ... si es que alguna vez (desafortunadamente, el desarrollo de Java se mueve muy lentamente hacia esa dirección. ..). –

3

No es redundante desde el punto de vista de la clase GenericRepository. Cuando tiene métodos como T get(K id), no puede saber qué tipos del argumento id puede aceptar de otro modo. Puede escribir lo siguiente:

interface GenericRepository<T extends Identifiable<?>> { 
    T get(Object id); 
} 

Ahora usted no tiene que escribir Long como un parámetro de tipo, pero se pierde la posibilidad de comprobar si se utiliza el método get correctamente en tiempo de compilación. Entonces la variable tipo sirve para un propósito específico.

Y en cuanto a la declaración del campo, cuando se tiene un tipo genérico, hay que especificar todas las variables de tipo que utiliza. Por supuesto, podría argumentar que sería bueno si el lenguaje pudiera entender que uno de los valores de los parámetros puede inferirse del otro, pero es discutible.

+0

Mire mi edición. Parece que el compilador puede inferir el tipo de alguna manera. – sinuhepop

+0

@sinuhepop, no, no inferir ningún tipo allí, solo comparando la igualdad de dos parámetros de tipo concreto y definido. (Un ejemplo simple de) type inference es [this] (http://docs.oracle.com/javase/tutorial/java/generics/gentypeinference.html) (implementado en Java 7). –

0

Si no me genéricos equivocadas todas serán compilados como si fueran simplemente Object. Ahora la sintaxis es (difícil) comprobada para asegurarse de que no está poniendo manzanas con naranjas porque se agregaron genéricos después del diseño inicial de Java. es por eso que los genéricos son tan limitados ...

-1

¿Por qué no se puede quitar segundo K de,

public interface GenericRepository<T extends Identifiable<K>, K> { 

Así que en lugar de tener que el anterior, podemos tener como

public interface GenericRepository<T extends Identifiable<K>> { 

Por esto podemos hacer lo que quieras hacer.

+0

Sí, pero desafortunadamente el compilador dice "K no se puede resolver con un tipo". – sinuhepop

0

que trabajaría con (es decir,compilar)

public interface GenericRepository<T extends Identifiable> { 
    T get(T id); 
} 

pero dice, no obstante, que Identificable es un tipo sin procesar y que debe ser parametrizado.

Espero que ayude.

+0

Esto no es lo mismo: has declarado que 'get' toma un argumento de tipo' T' en lugar de 'K'. Podría decirse que ese es el quid de la cuestión, cómo tener una referencia 'K' dentro de la clase sin tener que declararlo cuando se construye, por lo que es un poco atrevido evitarlo. :) –

+0

Diría que no es posible tener una referencia K dentro de la clase sin tener que declararla al construir. La única otra cosa en la que podría pensar sería en la interfaz pública GenericRepository > { T get (T id); } – aretai

+1

Sí, esa es la pregunta: "** ¿Es posible? Si no, ¿por qué no? **" –

1

No hay mucho que hacer, aparte de introducir una interfaz que solo refina GenericRepository

public interface LongKeyedRepository<T extends Identifiable<Long>> 
     extends GenericRepository<T, Long> { { 
    //No new methods need to be defined 
    } 

Entonces usted puede tener

private LongKeyedRepository<MyEntity> myEntityRepository; 

etc.

+0

Gracias. Actualmente estoy haciendo algo similar a esto (he simplificado un poco mi estructura). Pero no me gusta demasiado, y no entiendo por qué es necesario. – sinuhepop

Cuestiones relacionadas