简体   繁体   中英

How do I use the generic type of this class's generic type (Java)?

I have an abstract class A<T> with generic wildcard T. There are two extensions of A,

public class A1 extends A<Integer> {...}

and

public class A2 extends A<String> {...}

Now I have another class, let's call it B, that is basically a collection of either A1 or A2. So I defined it as a class of generic type T extends A:

public class B<T extends A> {...}

What I would like to be able to do is, within B, create methods that return the type of T's generic. For example,

B<A1> foo = new B<A1>;
foo.get(); // returns Integer, corresponding to A1's generic type

and

B<A2> bar = new B<A2>;
bar.get(); // returns String, corresponding to A2's generic type

Is it possible to do this in Java? I'm having trouble figuring out what return type to put when declaring B's methods (if I wanted B to return T, I'd put public T get() {...} , but I actually want to return the parametrized type of T). Or is there a pattern that solves this problem better than the way I'm approaching it?

Thanks.

You can declare a second type parameter for B and have that be the parameterization of A

class B<K, T extends A<K>> {
    public K get() {
        // actual implementation
        return null;
    }
}

Then declare your variables

B<String, A2> var = new B<>();
String ret = var.get();

What is a raw type and why shouldn't we use it?

I would like to provide an example that may help you better understand why we would need two generic type parameters. First, the sample declarations of the A and its direct subclasses .

class A<T> {

    private T value;

    public A(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }   
}

class A1 extends A<Integer> {
    A1(Integer i) {
        super(i);
    }
}

class A2 extends A<String> {
    A2(String str) {
        super(str);
    }
}

Now, the B class declaration. Notice, how it references both the generic types in its definition to provide access to the members of the wrapped A subclass.

public class B<T, X extends A<T>> {

    private X data;

    public B(X data) {
        this.data = data;
    }

    public T get() {
        return data.getValue();
    }

    public static void main(String[] args) {

        B<Integer, A1> foo = new B<Integer, A1>(new A1(10));
        System.out.println(foo.get()); // returns Integer

        B<String, A2> bar = new B<String, A2>(new A2("Ten"));
        System.out.println(bar.get()); // returns String
    }
}

If you switch to just one type parameter, you have two options.

You either keep the type of the A subclass like

public class B<T extends A<?>> {

    private T data;

    public B(T data) {
        this.data = data;
    }

    public Object get() {
        return data.getValue();
    }

    ...
}

Or, the actual return type of the getter.

public class B<T> {

    private T value;

    @SuppressWarnings("unchecked")
    public B(A<?> data) {
        this.value = (T) data.getValue();
    }

    public T get() {
        return value;
    }

    ... 
}

Notice, how both the solutions are fragile.

One returning an Object and requiring the use of instanceof to cast it properly and the other even more brittle with an unchecked cast. Since, you're not providing both the generic types you need, you've constrained your class design and run the risk of getting a ClassCastException now.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM