繁体   English   中英

了解Java泛型中的通配符

[英]Understanding wildcards in Java generics

我不确定为什么以下代码中的最后一个语句是非法的。 Integer应该是一个子类型? 那么为什么我不能把它分配给b

List<String> a = new ArrayList<String>();
a.add("foo");

// b is a List of anything
List<?> b = a;

// retrieve the first element
Object c = b.get(0);
// This is legal, because we can guarantee
// that the return type "?" is a subtype of Object

// Add an Integer to b.
b.add(new Integer (1)); 

关键是b指的是某种类型的列表,但编译器不知道该类型是什么,所以它不知道向它添加一个Integer是否有效。 同样也是一件好事,给出你的例子 - 你要将一个Integer添加到最初创建的对象中以保存字符串列表。 当然,在Java中执行时会丢失该信息 - 但编译器会尽量保证您的安全。

Java泛型常见问题进行了大量的详细信息。

Integer不是一个子类型? (一定)。 ? 是一个通配符; 你应该把它解释为“未知”。

因此List<?>List<Object> 可以将任何您喜欢的内容添加到List<Object>

引用“b”被声明为List,即“我还不知道的事物列表”。 您可以为此引用分配几乎任何实现,例如List。 这就是禁止通过此引用向列表添加任何内容的原因。

以下是关于泛型可以做什么和不做什么的简短摘要:

    List<? extends Number> listOfAnyNumbers = null;
    List<Number> listOfNumbers = null;
    List<Integer> listOfIntegers = null;
    listOfIntegers = listOfNumbers;     // Error - because listOfNumbers may contain non-integers
    listOfNumbers = listOfIntegers;     // Error - because to a listOfNumbers you can add any Number, while to listOfIntegers you cannot.
    listOfIntegers = listOfAnyNumbers;  // Error - because listOfAnyNumbers may contain non-integers  
    listOfAnyNumbers = listOfIntegers;  // OK    - because listOfIntegers is a list of ?, where ? extends Number.
    listOfNumbers = listOfAnyNumbers;   // Error - because listOfAnyNumbers may actually be List<Float>, to which you cannot add any Number. 
    listOfAnyNumbers = listOfNumbers;   // OK    - because listOfNumbers is a list of ?, where ? extends Number.

这是因为我们不能保证Integer是参数类型“?”的子类型。

看看这个:

Object c = b.get(0);

这是有效的,因为? 将永远是Object的子类型。

集合和泛型的粗略经验如下:

  • Collection<Foo>是一个Collection,你可以从中获得一个Foo,你可以添加一个Foo。
  • Collection<? extends Foo> Collection<? extends Foo>是一个Collection,你可以从中获得一个Foo, 但是你不能添加任何东西

为什么会这样? 因为当你说Collection<Foo> ,你向那个引用的用户承诺 ,他们可以在有问题的对象上调用add(Foo elem)方法。 另一方面,当您使用通配符版本时,您将“真实”参数类保留为引用用户的秘密 - 他们知道从集合中提取的任何元素都可以强制转换为Foo,而不是他们可以添加任何Foo。

为什么这有用? 因为有许多,很多很多情况下你会编写想要遍历Collection的方法,其元素都是Foos,但你永远不需要添加任何元素。 像这样:

public Foo findAFooThatILike(Collection<? extends Foo> foos);

在这里使用通配符意味着该方法将接受Collection<Foo>作为其参数以及Collection<Foo>的任何子类型的集合; 例如,如果Bar是Foo的子类型,则上面的签名意味着您可以将Collection<Bar>传递给该方法。

另一方面,如果你写了这样的签名:

public Foo findAFooThatILike(Collection<Foo> foos);

...那么你将无法传入Collection<Bar>作为参数。 为什么? 因为要成为Collection<Foo> ,它需要支持add(Foo elem)方法,而Collection<Bar>则不需要。

请注意,这些经验法则仅适用于Collection接口和类。 (另请注意, Collection<? extends Foo>并不意味着“只读Foo集合”;当您不知道精确的元素类型时,许多从集合中删除元素的方法仍然有效)。

那么,回到原来的问题: List<?>List<? extends Object>相同List<? extends Object> List<? extends Object> 它是一个列表,您可以从中获取对象实例的引用,但您无法安全地添加任何内容。

List <?>表示键入未知类型的列表。这可以是Integer,String或XYZ类的List。

由于您不知道键入的列表类型是什么,因此您只能从集合中读取,并且您只能将读取的对象视为Object实例。

如果要在通配符通用集合中插入元素,请转到超级通配符边界。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM