具有未知泛型接口的Java通用编程

时间:2011-09-04 15:17:45

标签: java generics interface raw-types

我正在使用几个带有泛型类型的接口。将它组合在一起时,当我不得不从不知道泛型参数的具体类型的部分代码中使用它时,我遇到了一些问题。

假设我有以下界面:

public interface MyObjectInterface<T extends Number> {}

实现该接口的对象存储在具有相同泛型类型的泛型集合中:

public interface MyCollectioninterface<T extends Number> {
    public void updateObject(MyObjectInterface<T> o);
}

MyCollectionInterface的具体实例包含相同泛型参数的几个MyObjectInterface:

public class ConcreteCollection<T extends Number> implements
 MyCollectionInterface<T> {
    List<MyObjectInterface<T>> list;
    public void updateObject(MyObjectInterface<T> o){}

}

现在,我有几个关于如何从客户端类使用这些通用接口的问题 (并且必须)不知道具体类型的泛型。

假设我有以下课程:

public class ClientClass{

    private MyCollectionInterface<?> collection;  //1st possibility
    private MyCollectionInterface collection;  //2nd possibility

    public ClientClass(MyCollectionInterface<?> collection){
        this.collection = collection;
    }

    public void foo(MyObjectInterface<?> o){
         this.collection.updateObject(o);  //this doesn't compile
    }
    public void foo(MyObjectInterface<? extends Number> o){
         this.collection.updateObject(o);  //this doesn't compile either
    }
    public void bar(MyObjectInterface o){
         MyObject b = o; //warning
         this.collection.updateObject(o);  //this compile but with warnings
    }
}

第一个问题

  1. 考虑到ClientClass不关心扩展Number是哪个具体类型的集合,我是否应该使用或不使用“?”来声明集合。 ?如果我使用第二个版本,我会收到以下警告:
  2.   

    MyCollectionInterface是原始类型。对泛型类型的引用   LatticeInterface应该参数化

    第二个问题

    1. 为什么方法foo不能编译?
    2. 第三个问题

      1. 似乎我需要使用bar签名来调用updateObject方法。无论如何,此解决方案在尝试分配MyObjectInterface参数时会产生警告,就像在第一个问题中一样。我可以删除此警告吗?
      2. 上次提问

        1. 我是否对这些通用接口做了一些奇怪的事情,我应该重构我的代码?
        2. 我真的要关心所有这些警告吗?
        3. 如何从一个我不知道具体类型的类中使用安全通用接口?

1 个答案:

答案 0 :(得分:1)

好的,我玩了一些代码并得出结论。

问题是您的ConcreteCollection(及其接口MyCollectionInterface)将方法updateObject声明为接收MyObjectInterface<T>类型的参数(其中T extends Number) - 请注意,类型是具体的(不是通配符)。

现在,在您的客户端类中,您收到一个集合并将其存储为MyCollectionInterface<?> ,但传递给ClientClass'构造函数的实例将是具体类型,例如:

new ClientClass(new ConcreteCollection<Integer>());

这意味着该实例的方法updateObject只接受MyCollectionInterface<Integer>类型的参数。

然后,在方法foo中,您尝试将MyObjectInterface<?>传递给updateObject,但由于编译器不知道您的集合接受哪种泛型类型(它可能是{{} 1}}在我的示例中,但它也可能是Integer或任何其他类型Double},将不允许传递任何对象

简而言之,如果您将引用声明为Number,则无法在其上调用MyCollectionInterface<?>。所以你有两个选择:

1)选择具体类型并坚持下去:

updateObject

但是你限制了你可以在构造函数中收到的集合(这可能不是一个坏主意),或者:

2)修改您的界面以接受通配符类型:

private MyCollectionInterface<Number> collection;

public ClientClass(MyCollectionInterface<Number> collection){
    this.collection = collection;
}

public void foo(MyObjectInterface<Number> o){
     this.collection.updateObject(o);  //compiles
}

另请注意,即使在 2)中,除非您声明public interface MyCollectionInterface<T extends Number> { public void updateObject(MyObjectInterface<? extends Number> o); } public class ConcreteCollection<T extends Number> implements MyCollectionInterface<T> { List<MyObjectInterface<T>> list; public void updateObject(MyObjectInterface<? extends Number> o) {} } private MyCollectionInterface<?> collection; public ClientClass(MyCollectionInterface<?> collection){ this.collection = collection; } public void foo(MyObjectInterface<?> o){ this.collection.updateObject(o); //compiles } 这样的内容,否则在updateObject方法的实现中仍会遇到同样的问题(以list为例):

ArrayList

在这种情况下,您也可以从List<MyObjectInterface<? extends Number>> list = new ArrayList<MyObjectInterface<? extends Number>>(); <T extends Number>移除MyCollectionInterface,因为ConcreteCollection已不再使用。

@Your last questions:

1)可能是的 2)你应该 3)你不能,如果你真的不关心你在集合中存储哪些对象,你应该完全抛弃泛型。

很抱歉答案很长,希望这有帮助。