12

我有一个类表示一对相同类型的两个值(类型可以是一组特定类型中的任何一个):

  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 
      }
  }

这里显然有很多问题:

  1. 为了实例化变量,我应该以某种方式知道它的确切类型并调用该特定类型的构造函数;在最好的情况下,这意味着在构造函数中有一个相当大的 if else 语句,例如:

     public Pair(){
           if(var1 instanceof SpecificType1){
              var1 = new SpecificType1(); 
              var2 = new SpecificType2();
           }
      }
    
  2. 即使我按照上述方式进行操作,我也会遇到一些问题,因为 var1 被声明为 E 类型,并且在尝试实例化 SpecficType1 并将结果对象分配给 var1/var2 时会出现类型不匹配错误。为了使其工作,我必须转换为 E :

       var1 = (E)new SpecificType1();
    

但这破坏了编译时类型检查,因为我试图将特定类型转换为泛型类型。

这是 Java 中泛型的限制,还是这种情况不适合使用泛型?

4

3 回答 3

7

为了实例化变量,我应该以某种方式知道它的确切类型并调用该特定类型的构造函数;在最好的情况下,这意味着在构造函数中有一个相当大的 if else 语句,例如:

在此之前你会遇到问题。

   if(var1 instanceof SpecificType1){
      var1 = new SpecificType1(); 
      var2 = new SpecificType2();
   }

var1null这一点上,var1 instanceof Tfalse所有人都是如此T


Java 泛型的一个限制是泛型类型参数被删除,因此您无法从零参数构造函数中反映类型参数。

调用者必须提供一些上下文来告诉您如何初始化var1and var2,提供该上下文的典型方法是通过构造函数参数。


您最好的选择可能是让var1var2开始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

于 2012-09-22T23:09:35.803 回答
2

您不能实例化泛型类型 - 例如,如果泛型类型是 会发生什么SomeAbstractClass?什么会被实例化?(这不是原因,只是直觉)

但是,您可以使用java 反射 API来实例化该对象 - 但您将需要特定的类对象。

一个更优雅的替代方案是使用抽象工厂设计模式,并将工厂对象传递给您的配对,并使用它来构造所需的对象。


代码示例:

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++);
    }
}
于 2012-09-22T23:11:35.863 回答
1

如果框架要实例化它,它将作为原始类型进行,相当于new Pair()没有类型参数。

我想你必须创建简单的单线类,如:

class SpecificType1Pair extends Pair<SpecificType1> {}

并将它们传递给框架。您可以将实际类型参数作为getClass().getGenericSuperclass()).getActualTypeArguments()[0]. 你的班级对看起来像这样:

public abstract class Pair<E extends AClass> {
    private E var1;
    private E var2;

    public Pair() {
        ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
        @SuppressWarnings("unchecked")
        Class<E> clazz = (Class<E>) superclass.getActualTypeArguments()[0];
        try {
            var1 = clazz.newInstance();
            var2 = clazz.newInstance();
        } catch (InstantiationException e) {
            handle(e);
        } catch (IllegalAccessException e) {
            handle(e);
        }
    }
}
于 2012-09-23T00:28:55.827 回答