我有一个类代表一对相同类型的两个值(类型可以是任何特定类型的集合):
public class Pair<E extends AClass>{
private E var1;
private E var2;
}
这个类由框架使用,所以它需要一个无参构造函数,我必须在其中实例化2个变量(var1,var2):
public class Pair<E extends AClass>{
private E var1;
private E var2;
public Pair(){
var1 = invoke constructor of type E;
var2 = invoke constructor of type E
}
}
这里显然有很多问题:
为了实例化变量,我应该以某种方式知道它的确切类型并调用该特定类型的构造函数;在最好的情况下,这意味着在构造函数中有一个非常大的if else语句,如:
public Pair(){
if(var1 instanceof SpecificType1){
var1 = new SpecificType1();
var2 = new SpecificType2();
}
}
即使我如上所述,我也会遇到一些问题,因为var1被声明为E类,我在尝试实例化SpecficType1并将结果对象分配给var1 / var2时会遇到类型不匹配错误。为了使它成功,我必须转向E:
var1 = (E)new SpecificType1();
但是这会破坏编译时类型检查,因为我正在尝试将特定类型转换为泛型类型。
这是Java中Generics的限制,还是使用Generics这个场景很糟糕?
答案 0 :(得分:6)
为了实例化变量,我应该以某种方式知道它的确切类型并调用该特定类型的构造函数;在最好的情况下,这意味着在构造函数中有一个非常大的if else语句,如:
在此之前你会遇到问题。
if(var1 instanceof SpecificType1){
var1 = new SpecificType1();
var2 = new SpecificType2();
}
此时 var1
为null
,因此对于所有var1 instanceof T
,false
为T
。
Java泛型的一个限制是泛型类型参数是erased,所以你无法从零参数构造函数中反映出类型参数。
调用者必须提供一些上下文来告诉您如何初始化var1
和var2
,并且提供该上下文的典型方法是通过构造函数参数。
您最好的选择可能是让var1
和var2
从null
开始,然后延迟初始化,直到您获得所需的上下文为止。
也许
void init(Class<E> type) {
if (type.isAssignableFrom(ConcreteType1.class)) {
var1 = type.cast(new ConcreteType1(...));
var2 = type.cast(new ConcreteType1(...));
} else { /* other branches */ }
}
这并不完美,因为您仍然无法将E extends List<String>
与E extends List<Number>
区分开来,但对于您的情况可能已经足够了,.cast
方法会为您提供类型 - 安全施放到E
。
或者,Guava,Guice和相关库提供Supplier<E>
接口之类的东西,它可以在init
方法中派上用场。
答案 1 :(得分:2)
您无法实例化泛型类型 - 例如,如果泛型类型为SomeAbstractClass
会发生什么?什么将被实例化? (这不是原因,只是直觉)
但是,您可以使用 java reflection API 来实例化对象 - 但是您需要特定的类对象。
更优雅的选择是使用 abstract factory design pattern ,并将工厂对象传递给您的对,并使用它来构造所需的对象。
代码示例:
public class Pair<S> {
public final S var1;
public final S var2;
public Pair(Factory<S> builder) {
var1 = builder.build();
var2 = builder.build();
}
}
public interface Factory<S> {
public S build();
}
public class IntegerBuilder implements Factory<Integer> {
private int element = 5;
public Integer build() {
return new Integer(element++);
}
}