在这种情况下如何避免泛型?

时间:2017-06-09 05:43:00

标签: java generics

class Attribute<T>{
   private T attr;
   public Attribute(T attr){
      this.attr = attr;
   } 
}

Class Matrix<T>{
   private String name;
   List<Attribute<T>> list;

   public Matrix(String name, T t){
     this.name = name;
     list = new ArrayList<>();
     list.add(new Attribute<T>(t)); 
  }
}

 interface Extractor<T> {
    public List<Matrix<T>> extract();
}

InfoExtractor implements Extractor<String>{

    public List<Matrix<String>> extract(){ 
       List<Matrix<String>> matrixList = new ArrayList<>();

        // The problem is here!!!!
        matrixList.add(new Matrix<String>("abc")); 
    }
 }

 Document<T>{
      Map<String, List<Matrix<T>>> matrixMap;

        public void process(){
        ...
         Extractor<T> extractor = (Extractor<T>) new StringExtractor(sent);
        List<Matrix<T>> matrix = extractor.extract(...);
   }

我的问题是,有没有办法避免将Matrix定义为泛型类型?我想避免的原因是“List<Attribute<T>>”在多个其他类中使用,可以是私有成员变量,也可以是方法返回类型。由于属性,似乎我必须将一些其他相关类定义为泛型类型,这会导致我的问题。

在这种情况下,有没有办法不将Matrix定义为泛型,但将“list”变量保留为泛型类型?

2 个答案:

答案 0 :(得分:2)

您的问题不在于通用实现,而在于其用法:

class InfoExtractor implements Extractor{

    // The problem is actually here
    public <T> List<Matrix<T>> extract(){ 
       List<Matrix<T>> matrixList = new ArrayList<>(); //and here

        // "The problem is here!!!!"
        matrixList.add(new Matrix<String>("abc")); 
    }
}

<T>表示您绑定了一个相对于方法调用的新泛型类型。简而言之,一种新的T类型仅适用于此方法的执行。您还可以创建List<Matrix<T>>,然后尝试添加新的Matrix<String>。如果您知道列表属于Matrix<String>类型,那么您可以在InfoExtractor中指定:

//If you cannot generify the interface for some reason
interface Extractor {
    public List<? extends Matrix<?>> extract(); //explained at bottom
}

//IDEALLY, then implement Extractor<String> instead
interface Extractor<T> {
    public List<Matrix<T>> extract();
}

class InfoExtractor implements Extractor { //or Extractor<String>

    public List<Matrix<String>> extract() {
        List<Matrix<String>> matrixList = new ArrayList<>();
        matrixList.add(new Matrix<>("abc"));
        return matrixList;
    }
}

当然,您可以看到Extractor的方法签名已更改。由于在返回类型上使用嵌套泛型,在编译时类型匹配会让事情变得有些混乱。矩阵的<?>是相当不言自明的,我们在矩阵内部返回多个且可能未知的类型。

通过在? extends Matrix上指定Extractor,我们会指定正确的差异。具体泛型是不变,因此List<Toyota>不是List<Car>,即使ToyotaCar的子类。如果我们希望Matrix协变,那么遗憾的是我们还需要Matrix上的边界。这实际上意味着忽略了Liskov的替换主体,我们基本上引用了提取器的具体子类(InfoExtractor ex vs Extractor ex),特别是从可用性的角度来看(作为具体类可以返回正确的类型安全,而接口不能)。

当您将列表中的矩阵<T>指定为类级泛型类型时,这当然会得到更加清晰/干净的处理。

答案 1 :(得分:0)

这取决于你想做什么。

如果您打算使用Matrix来保存单一类型的属性,那就是T,那么最好的方法似乎就是您在编译时进行类型检查时发布的方法。

如果从Matrix中删除泛型类型T,则无法知道每个属性的类型。您可以从Matrix声明中完全删除T,并使用通配符作为Attribute变量。虽然这将允许您在同一Matrix对象中存储不同T类型的属性,但它将删除编译类型检查。换句话说,使用Matrix的类必须知道期望什么类,并且可能将每个属性转换为期望的类。

Class Matrix{
   private String name;
   List<Attribute<?>> list;

   public Matrix(String name, T t){
     this.name = name;
     list = new ArrayList<>();
     list.add(new Attribute<>(t)); 
  }
}

添加一个Class变量并将Class对象保存在属性中,可以帮助您在运行时保留一些信息,以识别属性中对象的类。

class Attribute<T>{
   private T attr;
   private final Class<T> clazz;

   public Attribute(T attr, Class<T> clazz){
      this.attr = attr;
      this.clazz=clazz;
   } 

    public Attribute(T attr){
      this.attr = attr;
      Objects.requireNonNull(attr);
      this.clazz= (Class<T>) attr.getClass();
   } 
}

使用这种方法,原始构造函数只能用于非null值,否则类对象应该由程序员提供。