[英]What is the difference between ArrayList<?>, ArrayList, ArrayList<Object>?
[英]What is the practical difference between “ArrayList<A>” and “ArrayList<? extends A>”?
我看了问题q1 , q2 , q3 ,但它们并没有完全涵盖我的问题。
注意ArrayList<A> and ArrayList<? extends A>
ArrayList<A> and ArrayList<? extends A>
用于声明变量或参数(不用于创建新的泛型类)。
在声明对象属性时,两个表达式是否等效(案例1)?:
class Foo {
private ArrayList<A> aList; // == ArrayList<? extends A> aList;
}
编辑:从允许将什么类型的对象添加到aList
的角度来看,两个表达式是否相同?但是与以下情况的意义不同?
但在参数声明(案例2)中使用时它们是不同的?:
void methodFoo(ArrayList<A> al) != void methodFoo(ArrayList<? extends A> al)
因为第一个只允许传递ArrayList对象而第二个就像“更宽容”允许发送ArrayList<A1>
和ArrayList<A2>
(只要A1和A2扩展A)?
如果这是正确的,那么这两个表达式是否有效地存在其他情况?
谢谢,
我们来看看一些实际的例子。 说,你有:
List<Number> list;
这意味着分配给此变量或字段的任何内容都需要Number
并输出Number
,因此您始终知道会发生什么。 由于Integer
扩展了Number
,因此可以将Integer
添加到此列表中。 但是,您不能将ArrayList<Long>
分配给此列表。
但请考虑这种情况:
List<? extends Number> list;
这个说:嘿,这是一个扩展Number
的东西的列表,但没有人知道究竟是什么。 这是什么意思? 这意味着您可以将ArrayList<Long>
分配给此列表,这在第一种情况下是不可能的。 你仍然知道无论这个列表输出什么都是一个Number
,但你不能再将一个Integer
放入其中。
还有一个相反的情况:
List<? super Number> list;
通过印刷,你说:这是清单Number
或其超。 这就是反之亦然的一切。 该列表现在可以引用ArrayList<Object>
和ArrayList<Number>
。 现在我们不知道这个列表会输出什么。 它会是一个Number
吗? 它会成为一个Object
吗? 但是,现在我们知道,我们可以把一个Number
在该列表以及任何子Number
像Integer
或Long
。
顺便说一下,有一条规则说生产者延伸,消费者超级 (简称PECS)。 如果您需要列表来输出值,那么它是生产者,这是第二种情况。 如果您需要列表接受值,那么它是消费者,这是第三种情况。 如果您需要两者,请不要使用通配符(这是第一种情况)。
我希望这能解决问题。
这将解释不同之处:
public class GenericsTest {
private ArrayList<A> la;
private ArrayList<? extends A> lexta;
void doListA(ArrayList<A> la) {}
void doListExtA(ArrayList<? extends A> lexta) {}
void tester() {
la = new ArrayList<SubA>(); // Compiler error: Type mismatch
doListA(new ArrayList<SubA>()); // Compiler error: Type mismatch
lexta = new ArrayList<SubA>();
doListExtA(new ArrayList<SubA>());
}
static class A {}
static class SubA extends A {}
}
如您所见,调用方法并分配变量/实例字段具有相同的规则。 查看方法调用,将其参数赋值给其声明的参数。
ArrayList<A>
表示特定的A类,其中ArrayList<? extends A>
ArrayList<? extends A>
表示类A或任何一个使用A(Sub的A类)的类,这使它更通用
使用private ArrayList<A> aList;
因为变量声明并不等同于使用通配符private ArrayList<? extends A> aList;
private ArrayList<? extends A> aList;
通配版本将允许您分配任何类型的ArrayLists,这些类型扩展A和A本身但拒绝向列表添加元素,因为它无法确定它是否是类型安全的。 另一方面,使用ArrayList<A>
,您只能分配类型A的ArrayLists(或ArrayList的扩展名),然后您可以添加A元素和任何扩展A的元素。
仅供参考:您应该更喜欢使用更抽象的类型来声明List<A>
或Collection<A>
等变量/参数。
一开始很难掌握,但继承不适用于泛型,即如果B扩展A,则List<B>
不是(不能分配) List<A>
的“子类”。 此外, List<? extends A>
List<? extends A>
不是(无法分配) List<A>
的“子类”。
主要区别在于,如果通用表单在基类(或接口)中的方法中用作参数或返回类型,则它允许更大范围的类型签名计为覆盖而不是重载。
例如,以下代码是合法的(在Java 7中):
interface A
{
List<? extends Number> getSomeNumbers();
}
class B implements A
{
@Override
public ArrayList<Integer> getSomeNumbers()
{
return new ArrayList<>();
}
}
枚举之间的区别<? 扩展ZipEntry>和Enumeration <ZipEntry>?
所有这一切都意味着,有时你可以编写代码,当其他人使用它时需要更少的强制转换。 这不仅应该减少他们必须做的打字,还要消除可能的失败。
当然,Java泛型的问题在于它们是以受向后兼容性约束的方式引入的。 因此,并非一切都是您认为应该的,并且细节变得非常毛茸茸的确切有效,哪些无效。
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ812
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.