众所周知,arraylist init。应该是这样的
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?extends 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
答案 0 :(得分:13)
您无法将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!
List<Integer>
类型可以保证其中包含的任何内容都是Integer
。这就是为什么你可以在没有施法的情况下从中获得Integer
。如您所见,如果编译器允许将List
等Number
类型的List<Integer>
分配给List<Integer>
,则保证会被破坏。
将List<?>
分配给List<? extends Number>
或?
类型的引用是合法的,因为Object
表示“给定类型的某个未知子类型”(其中)在?
的情况下,类型为Number
,? extends Number
为?
。
由于List
表示您不知道 null
将接受哪种特定类型的对象,因此除了? extends X
之外添加任何内容都是不合法的它。但是,您可以从中检索任何对象,这是使用? super X
有界通配符类型的目的。请注意,对于List<? super Integer>
有界通配符类型,情况正好相反... Integer
是“某个未知类型的列表,至少是List
的超类型”。虽然您不确切地知道它是List<Integer>
的确切类型(可能是List<Number>
,List<Object>
,Integer
),但您确实知道无论它是什么,{{可以添加{1}}。
最后,new ArrayList<?>()
不合法,因为当您创建像ArrayList
这样的参数化类的实例时,您必须提供特定的类型参数。您可以真正使用示例中的任何内容(Object
,Foo
,这无关紧要),因为在分配时,您永远无法向null
添加任何内容它直接指向ArrayList<?>
引用。
答案 1 :(得分:8)
关键在于引用和实例之间的区别以及引用可以承诺的内容以及实例可以实际执行的操作。
ArrayList<A> a = new ArrayList<A>();
此处a
是对特定类型实例的引用 - 恰好是A
的数组列表。更明确地说,a
是对数组列表的引用,它将接受A
并将生成A
s。 new ArrayList<A>()
是A
s数组列表的一个实例,即一个接受A
并将生成A
s的数组列表。
ArrayList<Integer> a = new ArrayList<Number>();
此处,a
是对Integers
的数组列表的引用,即完全是一个可以接受Integer
并且将生成Integer
s的数组列表。它不能指向Number
s的数组列表。 Number
的数组列表不能满足ArrayList<Integer> a
的所有承诺(即Number
的数组列表可能产生不是Integer
的对象,即使它然后空了。)
ArrayList<Number> a = new ArrayList<Integer>();
此处,a
的声明表示a
将完全引用Number
的数组列表,即恰好是接受Number
s的数组列表并将生成Number
s。它不能指向Integer
的数组列表,因为a
的类型声明表明a
可以接受任何Number
,但Integer
的数组列表} s不能接受任何Number
,它只能接受Integer
s。
ArrayList<? extends Object> a= new ArrayList<Object>();
此处a
是对类型系列的(通用)引用,而不是对特定类型的引用。它可以指向属于该系列成员的任何列表。然而,对于这个不错的灵活引用的权衡是,如果它是类型特定的引用(例如非泛型),它们不能保证它可以具有的所有功能。在这种情况下,a
是对生成 Object
的数组列表的引用。 但是,与特定于类型的列表引用不同,此a
引用不能接受任何Object
。 (即并非a
可指向的类型系列中的每个成员都可以接受任何Object
,例如Integer
的数组列表只能接受Integer
s。)
ArrayList<? super Integer> a = new ArrayList<Number>();
同样,a
是对一类类型(而不是单一特定类型)的引用。由于通配符使用super
,因此此列表引用可以接受Integer
,但它无法生成Integer
。换句话说,我们知道a
可以指向的类型系列中的任何一个成员都可以接受Integer
。但是,并非该家庭的每个成员都可以生成Integer
。
PECS - 制作人extends
,消费者super
- 此助记符可帮助您记住使用extends
表示通用类型可以生成具体类型(但不能接受)。使用super
表示通用类型可以使用(接受)特定类型(但不能生成它)。
ArrayList<ArrayList<?>> a
一个数组列表,其中包含对作为数组列表类型族成员的任何列表的引用。
= new ArrayList<ArrayList<?>>(); // correct
数组列表的实例,它保存对任何列表的引用,该列表是数组列表类型的成员。
ArrayList<?> a
对任何数组列表的引用(数组列表类型系列的成员)。
= new ArrayList<?>()
ArrayList<?>
是指数组列表类型系列中的任何类型,但您只能实例化特定类型。
答案 2 :(得分:2)
你有奇怪的期望。如果你给出了引导他们的论据链,我们可能会发现它们中的缺陷。事实上,我只能简单介绍一下泛型,希望触及你可能误解的几点。
ArrayList<? extends Object>
是一个ArrayList,其类型参数已知为Object
或其子类型。 (是的,类型边界中的扩展具有除直接子类之外的含义)。由于只有引用类型可以是类型参数,因此实际上相当于ArrayList<?>
。
也就是说,您可以将ArrayList<String>
放入使用ArrayList<?>
声明的变量中。这就是a1.add(3)
是编译时错误的原因。 a1
声明的类型允许a1
为ArrayList<String>
,无法添加Integer
。
显然,ArrayList<?>
不是很有用,因为你只能在其中插入null。这可能是Java Spec forbids的原因:
相反,如果有的话,这是一个编译时错误 类中使用的类型参数 实例创建表达式 通配符类型参数
ArrayList<ArrayList<?>>
是一种功能数据类型。您可以将各种ArrayLists添加到其中,并检索它们。由于ArrayList<?>
仅包含,但不是通配符类型,因此上述规则不适用。
答案 3 :(得分:1)
这很多与多态性有关。分配
时X = new Y();
X可以比Y更“特异”,但不是相反。 X只是你正在访问Y的句柄,Y是真正实例化的东西,
这里出现错误,因为Integer是一个Number,但Number不是Integer。
ArrayList<Integer> a = new ArrayList<Number>(); // compile-time error
因此,您调用的任何X方法必须对Y有效。由于X更普遍,它可能共享一些但不是所有Y的方法。但是,任何给出的论点必须对Y有效。
在使用add的示例中,int(小i)不是有效的Object或Integer。
ArrayList<?> a = new ArrayList<?>();
这不好,因为你实际上无法实例化包含?的数组列表。你可以这样声明一个,然后在new ArrayList<Whatever>();
答案 4 :(得分:0)
将?
视为"unknown"
。因此,"ArrayList<? extends Object>"
是指&#34;一种未知类型(或只要它)扩展Object&#34;。因此,需要说的是,arrayList.add(3)
会把你知道的东西变成一个未知的东西。 I.e&#39;遗忘&#39;。
答案 5 :(得分:-1)
ArrayList<Integer> a = new ArrayList<Number>();
不起作用,因为Number是Integer的超类这一事实并不意味着List<Number>
是List<Integer>
的超类。泛型在编译期间被删除,并且在运行时不存在,因此无法实现集合的父子关系:只删除有关元素类型的信息。
ArrayList<? extends Object> a1 = new ArrayList<Object>();
a1.add(3);
我无法解释为什么它不起作用。这真的很奇怪,但这是事实。语法<? extends Object>
实际上主要用于方法的返回值。即使在此示例中,Object o = a1.get(0)
也是有效的。
ArrayList<?> a = new ArrayList<?>()
这不起作用,因为您无法实例化未知类型的列表...