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();
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.