Java 泛型 - 数组列表初始化

2022-09-02 04:03:22

众所周知,数组列表初始化。应该是这样的

ArrayList<A> a = new ArrayList<A>();
ArrayList<Integer> a = new ArrayList<Number>(); // compile-time error

那么,为什么java允许这些?

1. ArrayList<? extends Object> a1 = new ArrayList<Object>();
2. ArrayList<?> a2 = new ArrayList<Integer>();

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

1. a1.add(3);
2. a2.add(3);

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

更一般

  1. a1.add(null e);
  2. a2.add(? e);

我读过这个,但很高兴听到你的消息。谢谢

另一个有趣的点是:

 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 

答案 1

不能将 分配给类型的引用,因为允许使用 除 以外的数字类型。如果您被允许这样做,则允许执行以下操作: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!

该类型保证它包含的任何内容都将是 .这就是为什么你可以在不施法的情况下从中得到好处。如您所见,如果编译器允许将另一种类型的 a 分配给该保证将被破坏。List<Integer>IntegerIntegerListNumberList<Integer>

将 a 赋给类型(如 或)的引用是合法的,因为该值意味着“给定类型的某个未知子类型”(其中类型在 just 和 的情况下)。List<Integer>List<?>List<? extends Number>?Object?Number? extends Number

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

最后,这是不合法的,因为当您创建参数化类的实例(如 )时,您必须给出一个特定的类型参数。你真的可以在你的示例中使用任何东西(,,这没关系),因为你永远无法添加任何东西,除了它,因为你直接把它分配给一个引用。new ArrayList<?>()ArrayListObjectFoonullArrayList<?>


答案 2

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

ArrayList<A> a = new ArrayList<A>();

下面是对特定类型的实例的引用 - 正好是 s 的数组列表。更明确地说,是对数组列表的引用,该数组列表将接受 s 并生成 s。 是 s 的数组列表的实例,即接受 s 并生成 s 的数组列表。aAaAAnew ArrayList<A>()AAA

ArrayList<Integer> a = new ArrayList<Number>(); 

这里,是对 的数组列表的引用,即恰好是一个可以接受 s 并生成 s 的数组列表。它不能指向 s 的数组列表。s 的数组列表不能满足 的所有承诺(即,数组列表 s 可能会生成不是 s 的对象,即使它当时是空的)。aIntegersIntegerIntegerNumberNumberArrayList<Integer> aNumberInteger

ArrayList<Number> a = new ArrayList<Integer>(); 

在这里,声明 of that 将恰好引用 s 的数组列表,即恰好是一个将接受 s 并生成 s 的数组列表。它不能指向 s 的数组列表,因为 的类型声明 say 可以接受任何 ,但 s 的数组列表不能只接受任何 ,它只能接受 s。aaNumberNumberNumberIntegeraaNumberIntegerNumberInteger

ArrayList<? extends Object> a= new ArrayList<Object>();

下面是对一组类型的(泛型)引用,而不是对特定类型的引用。它可以指向属于该家庭的任何列表。但是,这个很好的灵活引用的权衡是,如果它是特定于类型的引用(例如非泛型),它们无法承诺所有功能。在本例中,是对将生成 的数组列表的引用。但是,与特定于类型的列表引用不同,此引用不能接受任何 .(即,并非可以指向的类型家族中的每个成员都可以接受任何,例如,s的数组列表只能接受s。aaObjectaObjectaObjectIntegerInteger

ArrayList<? super Integer> a = new ArrayList<Number>();

同样,是对一系列类型(而不是单个特定类型)的引用。由于通配符使用 ,此列表引用可以接受 s,但不能生成 s。换句话说,我们知道,可以指向的类型家族的任何和每个成员都可以接受。但是,并非该家族的每个成员都可以产生s。asuperIntegerIntegeraIntegerInteger

PECS - 生产者 , 消费者 - 此助记符可帮助您记住,使用意味着泛型类型可以生成特定类型(但不能接受它)。使用意味着泛型类型可以使用(接受)特定类型(但不能生成它)。extendssuperextendssuper

ArrayList<ArrayList<?>> a

一个数组列表,它保存对作为数组列表类型系列成员的任何列表的引用。

= new ArrayList<ArrayList<?>>(); // correct

数组列表的一个实例,该实例保存对作为数组列表类型系列成员的任何列表的引用。

ArrayList<?> a

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

= new ArrayList<?>()

ArrayList<?>引用数组列表类型系列中的任何类型,但只能实例化特定类型。


另请参阅如何添加到 List<? 扩展了数字>数据结构?


推荐