2011-11-08 18 views
8

Este es un ejemplo que inventé como una simplificación de mi código real, por lo que me disculpo si es un poco artificial. Lo que me gustaría hacer es obtener efectivamente dos parámetros de tipo de un único argumento de tipo anidado. Estoy bastante seguro de que esto es imposible, pero pensé que lo probaría.Parámetros de tipo anidado en Java

//Not legal java code 
public class Foo<C extends Collection<T>> { //where T is another type parameter 
    private C coll; 

    public Foo(C coll) { 
     this.coll = coll; 
    } 

    public void add(T elem){ 
     this.coll.add(elem); 
    } 
    //UPDATED TO ADD GETTER 
    /** 
    * I may need to retrieve the collection again, or pass it 
    * on to another function that needs the specific C type 
    */ 
    public C getColl(){ 
     return coll; 
    } 
} 
... 
List<String> strings = new ArrayList<String>(); 
Foo<List<String>> foo = new Foo<List<String>>(strings); 
foo.add("hello"); 

sé que podría hacerlo mediante la adición de otro tipo de parámetro:

public class Foo<C extends Collection<T>,T> 

pero luego tengo que añadir el redundante:

Foo<List<String>,String> foo = new Foo<List<String>,String>(strings); 

Y en mi caso en el mundo real, mis genéricos a veces se pueden especificar en la cláusula Implements como

public class Bar implements Baz<String> 

Tener que especificar ese segundo parámetro de tipo es aún más doloroso, porque parece que arroja los detalles de implementación en mi cara. Tener que decir

Foo<Bar,String> 

cuando ya existe una relación entre String y Bar, parece poco elegante. Entiendo que es Java, así que eso va con el territorio, pero es curioso si hubo una solución para esto.

Respuesta

6

No es posible y no creo que sea ideal de todos modos porque no hay nada en su clase existente que requiera invariancia.

Foo<T,C extends Collection<T>> 

podría ser más general

Foo<T,C extends Collection<? super T>> 

si la única razón para tener T es permitir que la mutación de la colección.

Nota, si usted está preocupado por tener que especificar dos parámetros de tipo de frecuencia, puede crear una subclase de poca profundidad:

class DerivedFoo<T> extends Foo<Collection<T>,T> 

y puede utilizar métodos de fábrica para evitar tener que hacer doble especifica durante la creación

public static <T> Foo<Collection<T>,T> fromCollection(Collection<T> c) 

también puede abstraer la interfaz en un interface para obtener los beneficios de tipos concisos que se obtiene con DerivedFoo anteriormente.

+0

La idea del método de fábrica es interesante, pero aún me molesta que el tipo se especifique dos veces, aunque mi código dicta que siempre serán las mismas. –

+0

@RusselLeggett, vale, entonces necesitas invarianza entonces, no 'Colección '? Sí. Eso es un dolor. Mi consejo sería tratar con la complejidad de su biblioteca y tratar de exponer las API con fábricas cuyo tipo de devolución es 'interfaz Foo extiende ComplicatedFoo , T>' para que los clientes puedan usar la versión de un solo parámetro. –

2

¿por qué no sólo tiene que utilizar T como su parámetro de tipo solamente, como en:

public class Foo<T> { //where T is another type parameter 
private Collection<T> coll; 

public Foo(Collection<T> coll) { 
    this.coll = coll; 
} 

public void add(T elem){ 
    this.coll.add(elem); 
} 
+0

Tengo curiosidad por ver por qué esta no sería la solución también. – ty1824

+3

Como dije, es un ejemplo artificial, pero ¿y si el tipo de colección que entró también importó? Podría ser una lista o un conjunto o una TreeList. Digamos que agregué un getter para recuperar la colección nuevamente - el tipo sería importante. –

2

Antes de Java7, los constructores no hago la inferencia de tipos, la solución es tener un método de fábrica estática. Eso ya no es necesario. En Java 7 puede

Foo<List<String>,String> foo = new Foo<>(strings); 

En cuanto T y C, si tenemos parámetros de tipo 2 con restricciones entre ellos, no tiene que haber cierto grado de redundancia. En su ejemplo, dado que un parámetro C dicta totalmente otro parámetro T, la redundancia parece insoportable. No veo una solución.

Pero es probable que pueda sentirse mejor si se reordenan los parámetros de tipo

Foo<String,Bar> foo = new Foo<>(bar); 

así que declaran String primera; luego proporcione un Baz<String> que es Bar

Cuestiones relacionadas