Java泛型 - ArrayList初始化

20 java generics arraylist

众所周知,arraylist init.应该是这样的

ArrayList<A> a = new ArrayList<A>();
ArrayList<Integer> a = new ArrayList<Number>(); // compile-time error
Run Code Online (Sandbox Code Playgroud)

所以,为什么java允许这些?

1. ArrayList<? extends Object> a1 = new ArrayList<Object>();
2. ArrayList<?> a2 = new ArrayList<Integer>();
Run Code Online (Sandbox Code Playgroud)

那么,如果他们是正确的,为什么不允许这些呢?

1. a1.add(3);
2. a2.add(3);
Run Code Online (Sandbox Code Playgroud)

编译器消息是:类型ArrayList中的方法add(int,capture#1-of?extends Object)不适用于参数(int)

更一般

  1. a1.add(null e);
  2. a2.add(? e);
Run Code Online (Sandbox Code Playgroud)

我读到了这个,但很高兴收到你的来信.谢谢

另一个有趣的观点是:

 ArrayList<ArrayList<?>> a = new ArrayList<ArrayList<?>>(); // correct
 ArrayList<?> a = new ArrayList<?>(); // wrong. I know it's reason but I have some 
question in my mind that mentioned above 
Run Code Online (Sandbox Code Playgroud)

Col*_*inD 13

您不能将a指定给List<Number>类型的引用,List<Integer>因为它List<Number>允许除了以外的数字类型Integer.如果您被允许这样做,将允许以下内容:

List<Number> numbers = new ArrayList<Number>();
numbers.add(1.1); // add a double
List<Integer> ints = numbers;
Integer fail = ints.get(0); // ClassCastException!
Run Code Online (Sandbox Code Playgroud)

该类型List<Integer>保证其中包含的任何内容都是一个Integer.这就是为什么你可以在Integer没有施法的情况下获得它的原因.正如您所看到的,如果编译器允许List另一种类型(如Number分配给List<Integer>该保证)将被破坏.

分配List<Integer>到例如类型的基准List<?>List<? extends Number>是合法的,因为?装置(其中的类型是"给定的类型的一些未知的子类型" Object中的只是的情况下?Number在的情况下? extends Number).

由于?表示您不知道List将接受哪种特定类型的对象,因此添加除此之外的任何内容都是不合法的null.但是,您可以从中检索任何对象,这是使用有? extends X界通配符类型的目的.请注意,对于有? super X界通配符类型,情况正好相反... a List<? super Integer>是"某个未知类型的列表,至少是一个超类型Integer".虽然你不知道到底是什么类型的List是(可能是List<Integer>,List<Number>,List<Object>)你肯定知道不管它是什么,一个Integer可以被添加到它.

最后,这new ArrayList<?>()是不合法的,因为当您创建类似于参数化类的实例时ArrayList,您必须提供特定的类型参数.你可以在你的例子中使用任何东西(但是Object,Foo无关紧要),因为你将永远无法添加任何内容null,因为你将它直接分配给ArrayList<?>引用.


Ber*_*t F 8

关键在于引用和实例之间的差异以及引用可以承诺的内容以及实例可以实际执行的操作.

ArrayList<A> a = new ArrayList<A>();
Run Code Online (Sandbox Code Playgroud)

a是对特定类型实例的引用 - 恰好是As 的数组列表.更明确地说,a是对将接受As并将生成As 的数组列表的引用. new ArrayList<A>()是一个As 数组列表的实例,即一个接受As并将生成As 的数组列表.

ArrayList<Integer> a = new ArrayList<Number>(); 
Run Code Online (Sandbox Code Playgroud)

这里a是对数组列表的精确引用Integers,即完全是一个可以接受Integers并将生成Integers 的数组列表.它不能指向Numbers 的数组列表.Numbers的数组列表不能满足所有的承诺ArrayList<Integer> a(即Numbers 的数组列表可能产生不是Integers的对象,即使它是空的然后).

ArrayList<Number> a = new ArrayList<Integer>(); 
Run Code Online (Sandbox Code Playgroud)

在这里,声明a说,a将把正是数组列表NumberS,也就是说,正是将接受一个数组列表NumberS和将产生Number秒.它不能指向数组列表IntegerS,因为类型声明a说,a可以接受任何Number,但数组列表Integers不能接受随便什么Number,它只能接受Integer秒.

ArrayList<? extends Object> a= new ArrayList<Object>();
Run Code Online (Sandbox Code Playgroud)

a是对一系列类型的(通用)引用,而不是对特定类型的引用.它可以指向属于该系列成员的任何列表.然而,这个很好的灵活引用的权衡是,如果它是类型特定的引用(例如非泛型),它们不能承诺它可能具有的所有功能.在这种情况下,a是对将生成 Object s 的数组列表的引用.但是,与特定于类型的列表引用不同,此a引用不能接受任何引用Object.(即不是每个a可以指向的类型的成员都可以接受任何成员Object,例如Integers 的数组列表只能接受Integers.)

ArrayList<? super Integer> a = new ArrayList<Number>();
Run Code Online (Sandbox Code Playgroud)

同样,a是对一类类型(而不是单一特定类型)的引用.由于通配符使用super,此列表引用可以接受Integers,但它不能生成Integers.换句话说,我们知道可以指出的任何类型的家庭成员a都可以接受Integer.然而,并非该家庭的每个成员都能生产Integers.

PECS - 生产者extends,消费者super- 这个助记符可以帮助您记住使用 extends手段,泛型类型可以生成特定类型(但不能接受它).使用super意味着泛型类型可以使用(接受)特定类型(但不能生成它).

ArrayList<ArrayList<?>> a
Run Code Online (Sandbox Code Playgroud)

一个数组列表,其中包含对作为数组列表类型族成员的任何列表的引用.

= new ArrayList<ArrayList<?>>(); // correct
Run Code Online (Sandbox Code Playgroud)

数组列表的实例,其中包含对作为数组列表类型族成员的任何列表的引用.

ArrayList<?> a
Run Code Online (Sandbox Code Playgroud)

对任何数组列表的引用(数组列表类型系列的成员).

= new ArrayList<?>()
Run Code Online (Sandbox Code Playgroud)

ArrayList<?> 指的是数组列表类型系列中的任何类型,但您只能实例化特定类型.


另请参见如何添加到列表<?扩展Number>数据结构?