简体   繁体   English

我可以在列表声明中使用泛型通配符吗?

[英]Can I use generics wildcard in List declaration?

Consider the following codes: 考虑以下代码:

class Super {}
class Sub extends Super {}
class Test {
    public static void main(String[] args) {
        List<? extends Super> list = new ArrayList<Sub>(); //1
        list.add(new Sub()); //2
    }
}

Line 1 compiles successfully, but line 2 fails compilation: 第1行编译成功,但第2行编译失败:

The method add(capture#2-of ? extends Super) in the type List<capture#2-of ? extends Super> is not applicable for the arguments (Sub)

My questions are: 我的问题是:
1) Why does line 1 successfully compile? 1)为什么第1行成功编译?
2) Is line 1 a good practice in declaring a List (or other collections)? 2)第1行是声明列表(或其他集合)的一种良好做法吗?
3) Why does line 2 fail compilation, after list was declared to be type Sub in line 1? 3)为什么在第1行中将list声明为Sub类型后,第2行为什么编译失败?
4) Eclipse's autocompletion says only "null" elements are allowed in list now. 4)Eclipse的自动完成功能说,列表中现在仅允许“ null”元素。 Why? 为什么?

Thanks a lot! 非常感谢!

1) Why does line 1 successfully compile? 1)为什么第1行成功编译?

The first line compiles because List<Sub> is subclass of List<? extends Super> 第一行进行编译,因为List<Sub>List<? extends Super>子类List<? extends Super> List<? extends Super> and that can only be true if List does not allow you to add any new members to it. List<? extends Super> ,并且只有在List不允许您向其添加任何新成员的情况下才可以。

? means that you don't exactly know that it is a List<Sub> or List<Sub1> , so it is unsafe to allow adding of new elements into the list and so it does not allow it. 意味着您并不完全知道它是List<Sub>List<Sub1> ,因此允许将新元素添加到列表中是不安全的,因此也不允许这样做。

2) Is line 1 a good practice in declaring a List (or other collections)? 2)第1行是声明列表(或其他集合)的一种良好做法吗?

If you already know that it is going to be List<Sub> then I don't find any use, but wildcards are used much when you are passing the List around to other classes like Utilities. 如果您已经知道它将成为List<Sub>那么我找不到任何用处,但是当您将List传递给其他类(如Utilities)时,会使用通配符。

3) Why does line 2 fail compilation, after list was declared to be type Sub in line 1? 3)为什么在第1行中将list声明为Sub类型后,第2行为什么编译失败?

Because as I already explained it is unsafe to add any element to list when you dont know the exact type. 因为正如我已经解释的那样,当您不知道确切类型时,将任何元素添加到列表都是不安全的。

4) Eclipse's autocompletion says only "null" elements are allowed in list now. 4)Eclipse的自动完成功能说,列表中现在仅允许“ null”元素。 Why? 为什么?

Because null is of every reference type, this is why you can assign any object a value of null . 因为null是每种引用类型,所以这就是为什么您可以为任何对象分配null值的原因。

Always remember PECS (Producer Extends Consumer Super) rule by Josh Bloch when working with generics 使用泛型时,请务必记住Josh Bloch的PECS(生产者扩展消费者超级用户)规则

Good References: 好的参考文献:

Capturing declarations as in line 1 are good in method arguments. 如第1行所示,捕获声明在方法参数中很有用。 See Collection.addAll(Collection<? extends E>) . 请参见Collection.addAll(Collection<? extends E>) If you need a list with something extends Super , just use List<Super> . 如果您需要带有扩展Super的列表,只需使用List<Super>

You have declared your list as, it can be assigned to any sub type of Super class. 您已将list声明为,可以将其分配给Super类的任何子类型。 So, assigning a Sub type list is OK, Compiler allows to compile. 因此,分配一个Sub type列表就可以了,Compiler允许进行编译。 But that doesn't mean, you can add a specific Object type into it. 但这并不意味着您可以在其中添加特定的对象类型。

<? extends Super> <? extends Super> don't means, you can add any sub type of Super. <? extends Super>并非意味着,您可以添加Super的任何子类型。 It means, You can assign any sub type collection to it. 这意味着,您可以为其分配任何子类型集合。

1) Why does line 1 successfully compile? 1)为什么第1行成功编译?

You basically define the list to contain elements of any type that extends (or is) Super , ie the compiler knows that every element in that list should at least have the properties of Super . 您基本上将列表定义为包含扩展(或为Super )的任何类型的元素,即,编译器知道该列表中的每个元素至少应具有Super的属性。

Since Sub is a subclass of Super and thus any list containing only Sub elements also meets the requirement of all elements being instances of Super , List<? extends Super> list = new ArrayList<Sub>(); 由于SubSuper的子类,因此任何仅包含Sub元素的列表也满足所有元素都是Super实例的要求, List<? extends Super> list = new ArrayList<Sub>(); List<? extends Super> list = new ArrayList<Sub>(); is correct. 是正确的。

2) Is line 1 a good practice in declaring a List (or other collections)? 2)第1行是声明列表(或其他集合)的一种良好做法吗?

As a local variable that depends on personal style, IMHO. 作为取决于个人风格的局部变量,恕我直言。 When declaring parameters that way (or instance/static variables) that's often not only good style but also needed. 以这种方式声明参数(或实例/静态变量)时,通常不仅是好的样式,而且也是必需的。

Consider a method that iterates over a collection of numbers and returns a sum. 考虑一种迭代数字集合并返回总和的方法。 You could declare the parameter to be Collection<Number> but then you couldn't pass a Collection<Integer> without a nasty cast. 您可以将参数声明为Collection<Number>但是如果没有讨厌的转换,就无法传递Collection<Integer> If the parameter is declared as Collection<? extends Number> 如果参数声明为Collection<? extends Number> Collection<? extends Number> you can pass Collection<Integer> . Collection<? extends Number>您可以传递Collection<Integer>

3) Why does line 2 fail compilation, after list was declared to be type Sub in line 1? 3)为什么在第1行中将list声明为Sub类型后,第2行为什么编译失败?

The reason is that the compiler doesn't know the exact type of the elements in the list. 原因是编译器不知道列表中元素的确切类型。 Is it a list of Super or a list of Sub ? Super列表还是Sub列表?

As an example take List<? extends Number> list List<? extends Number> list为例 List<? extends Number> list . List<? extends Number> list You don't know whether you have a List<Number> , a List<Double> or a List<Integer> and thus can't tell whether list.add( new Integer(1) ); 您不知道您是否具有List<Number>List<Double>List<Integer> ,因此无法确定list.add( new Integer(1) ); would be ok or not. 会没事的。 That's how the compiler sees it. 编译器就是这样看的。

4) Eclipse's autocompletion says only "null" elements are allowed in list now. 4)Eclipse的自动完成功能说,列表中现在仅允许“ null”元素。 Why? 为什么?

I'd have to guess here but adding null to a list would be ok since no matter what type the actual list declares, you can always cast null to that type. 我不得不在这里猜测,但是将null添加到列表中就可以了,因为无论实际列表声明的是哪种类型,您都可以始终将null转换为该类型。

1) Why does line 1 successfully compile? 1)为什么第1行成功编译?

You declare list to be "a list of something that derives from Super ". 您将list声明为“源自Super的事物的列表”。 You assign a list of Sub to it. 您为其分配一个Sub列表。 Sub is "something that derives from Super ". Sub是“源自Super东西”。

2) Is line 1 a good practice in declaring a List (or other collections)? 2)第1行是声明列表(或其他集合)的一种良好做法吗?

No. Wildcards are for function parameters. 不可以。通配符用于功能参数。 Local variables should be as specific as possible in their generics arguments to avoid problems such as the one you're facing. 局部变量应在其泛型参数中尽可能具体,以避免出现您所面临的问题。

3) Why does line 2 fail compilation, after list was declared to be type Sub in line 1? 3)为什么在第1行中将list声明为Sub类型后,第2行为什么编译失败?

Fallacy. 谬论。 list was declared to have elements of "something that derives from Super ", not Sub as you claim. list被声明为具有“源自Super元素”的元素,而不是您声称的Sub And you can't add a Sub to the list because the "something" might be something other than Sub ; 而且您不能将Sub添加到列表中,因为“东西”可能不是Sub it might be Sub2 , and adding would be equivalent to this illegal assignment: 它可能是Sub2 ,并且添加等同于此非法分配:

class Super {}
class Sub extends Super {}
class Sub2 extends Super {}
Sub2 s = new Sub();

The key misunderstanding here seems to be that you think the wildcard somehow gets substituted on assignment. 此处的主要误解似乎是您认为通配符会在赋值时被替换。 It isn't. 不是。 It remains a wildcard, and only compatibility checks are done. 它仍然是通配符,并且仅进行兼容性检查。

4) Eclipse's autocompletion says only "null" elements are allowed in list now. 4)Eclipse的自动完成功能说,列表中现在仅允许“ null”元素。 Why? 为什么?

null is the only value that is of any possible reference type, and is therefore compatible with the list no matter what the wildcard stands for. null是唯一具有任何可能引用类型的值,因此无论通配符代表什么,都与列表兼容。

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

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