在Java中使用泛型时,为什么必须指定<! - ? - >?

时间:2016-05-23 07:39:37

标签: java generics

虽然我在C ++和其他语言方面有很多经验,但我在Java方面还是比较新的。所以模板/泛型不是我不知道的。

但是有些事情困扰着我,正是这个<?>我被告知每当我使用某些通用实例时我应该使用这些事情我不知道具体到哪个具体输入它将是:

像:

List< MyGeneric >    foo; // bad
List< MyGeneric<?> > bar; // good

当使用第一个表达式时,IntelliJ并没有对我说话,我也不明白它为什么要这样做。我的同事已经表示第二个表达式很多更好,但无法确切地告诉我原因。

我的意思是,这两者之间究竟有什么不同,除了第二部分明确表示它是我们操纵的通用这一事实?

编译器当然知道它在编译时是通用的,所以我的猜测第二个表达式只是更好,因为它告诉程序员他正在操作通用。

我是对的吗?

编辑:为了澄清,我提前使用限制性最强的类型,例如List<MyGeneric<Double>>,每当我事先知道我要存储在那里的内容时。我的问题是当我存储未知类型的泛型时。

4 个答案:

答案 0 :(得分:9)

每次 时间?它总是不适用,并不总是有意义。

让我们来描述实际情况:<?>http://wiki.qt.io/RaspberryPi2EGLFS,它立即暗示了两件事:

  • MyGeneric是一个通用类,但
  • 不知道它持有什么类型(并且可能没关系)。

第一个表达式首选,因为第一个表达式总是保证你将使用unbound wildcard,而你真的不喜欢我想使用原始类型。但是,假设每次使用未绑定的通配符都是理想的,这是一个严重的过度概括。

如果您实际知道或关心类型,或了解或关心其边界,请改用它。

答案 1 :(得分:4)

让我们举一个例子说明为什么使用第一个是不好的。假设MyGeneric定义如下:

class MyGeneric<T> {
  private final T instance;
  MyGeneric(T instance) { this.instance = instance; }
  T get() { return instance; }
}

以下代码将编译并运行,但在运行时使用ClassCastException

失败
List<MyGeneric> list = new ArrayList<>();
list.add(new MyGeneric<>("Hello"));

for (MyGeneric instance : list) { 
  Integer value = (Integer) instance.get();  // Compiles, but fails at runtime.
}

这是因为您正在使用原始类型进行编译:编译器不知道instance.get()无法返回Integer;它只会警告你它可能不安全。

另一方面,以下代码甚至无法编译:

List<MyGeneric<String>> list = new ArrayList<>();
list.add(new MyGeneric<>("Hello"));

for (MyGeneric<String> instance : list) { 
  Integer value = (Integer) instance.get();  // Won't compile, incompatible types.
}

答案 2 :(得分:3)

不同之处在于原始类型忽略该类是泛型的事实,而通配符<?>指定该类是泛型但类型参数未知。< / p>

Raw表示您丢失了所有编译器类型检查。通配符可以保持类型检查的完整性。

示例:

public class MyGeneric<T> {
    private T val;
    public T get() {
        return this.val;
    }
    public void set(T val) {
        this.val = val;
    }
}
MyGeneric a = new MyGeneric<Integer>();
a.set("Foo"); // accepted

编译器接受将a的值设置为String,因为Integer已定义为raw,这意味着编译器忽略了类是泛型的事实。当a稍后用作val时,程序将崩溃。这是一颗等待起飞的炸弹。

Integer

尝试设置MyGeneric<?> b = new MyGeneric<Integer>(); b.set("Bar"); // compile error 的值将无法编译:

b

这里编译器知道该类是通用的,并且不允许将值设置为任何值(即使是The method set(capture#1-of ?) in the type MyGeneric<capture#1-of ?> is not applicable for the arguments (String) ),因为它不知道允许哪种类型(通配符=未知,记得?)。编译器在这里应该保护它。

答案 3 :(得分:0)

List<?>表示键入未知类型的列表。这可以是List<A>List<B>List<String>等。

由于您不知道键入List的类型,您只能从集合中读取,并且您只能将读取的对象视为Object个实例。这是一个例子:

public void processElements(List<?> elements) {
    for(Object o : elements){
        System.out.println(o);
    }
}

现在可以使用任何通用processElements()作为参数调用List方法。例如List<A>List<B>List<C>List<String>等。以下是有效示例:

List<A> listA = new ArrayList<A>();

processElements(listA);

以下教程将进一步帮助您理解它: