useful - java generics wildcard example




¿Cómo puedo forzar que dos variables de instancia sean del mismo tipo genérico? (2)

Suponiendo que tengo una clase con dos variables de instancia de tipo genérico, ¿cómo puedo forzar a esos tipos a ser iguales?

No he encontrado ninguna pregunta similar en ningún otro lugar (y mucho menos en las respuestas), lo cual puede deberse a que estoy usando términos incorrectos. Hay una pregunta , pero no estoy seguro de cómo aplicarlo a mi situación.

En resumen, ¿cómo puedo obtener esta última línea de código para no compilar?

class Foo<T> {
    private T value;
}

class Bar {
    private Foo var1;
    private Foo var2;

    public Bar(Foo var1, Foo var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public static void main(String[] args) {
        Foo<String> var1 = new Foo<>();
        Foo<Integer> var2 = new Foo<>();
        Bar b = new Bar(var1, var2); // this should not work
    }
}

La respuesta de Lino es buena. Quiero añadir un hecho:

Si no tiene que realizar un seguimiento del tipo de var s, pero solo verificar que sean del mismo tipo, puede mover el parámetro type de la clase Bar a su constructor:

class Bar {
    // These will always have the same type, but
    // that's not visible here
    private Foo<?> var1;
    private Foo<?> var2;

    // The parameter types of the construct ensures that
    // the vars are always of the same type
    public <T> Bar(Foo<T> var1, Foo<T> var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public static void main(String[] args) {
        Foo<String> var1 = new Foo<>();
        Foo<Integer> var2 = new Foo<>();

        Bar b1 = new Bar(var1, var1); // Works fine
        Bar b2 = new Bar(var1, var2); // Compilation error
    }
}

Esta técnica funciona para todo tipo de métodos.


Make Bar también genérico:

class Bar<T> {
    private Foo<T> var1;
    private Foo<T> var2;

    public Bar(Foo<T> var1, Foo<T> var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public static void main(String[] args) {
        Foo<String> var1 = new Foo<>();
        Foo<Integer> var2 = new Foo<>();
        Bar<String> b = new Bar<>(var1, var2); // this does not work
    }
}

Al usar un parámetro genérico de nivel de clase T para Bar , puede imponer los mismos tipos para ambas variables de instancia.

Esto elimina también la advertencia de usar tipos brutos ( que nunca deben usarse ) como lo menciona @daniu en los comentarios.

Ahora, si no usa tipos brutos pero no desea los mismos tipos, puede trabajar con comodines:

Con los límites superiores, que solo permiten la lectura con tipo ( var1 y var2 siempre producirán una implementación de tipo T ):

class Bar<T> {
    private Foo<? extends T> var1;
    private Foo<? extends T> var2;

    public Bar(Foo<? extends T> var1, Foo<? extends T> var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public static void main(String[] args) {
        Foo<String> var1 = new Foo<>();
        Foo<Integer> var2 = new Foo<>();
        Bar<Object> b = new Bar<>(var1, var2); // this does now work
    }
}

Y con un límite inferior, que solo permite escritura escrita ( var1 y var2 siempre consumirán cualquier implementación de tipo T ):

class Bar<T> {
    private Foo<? super T> var1;
    private Foo<? super T> var2;

    public Bar(Foo<? super T> var1, Foo<? super T> var2) {
        this.var1 = var1;
        this.var2 = var2;
    }

    public static void main(String[] args) {
        Foo<Integer> var1 = new Foo<>();
        Foo<Number> var2 = new Foo<>();
        Bar<Integer> b = new Bar<>(var1, var2); // this does now work
    }
}

Para más información sobre ese tema, puede leer: ¿Qué es PECS (Producer Extends Consumer Super)?





generics