请注意,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)?
如果这是正确的,那么是否存在两个表达式的其他场景 有效的不同?
谢谢,
答案 0 :(得分:8)
让我们看一些实际的例子。说,你有:
List<Number> list;
这意味着分配给此变量或字段的内容需要Number
并输出Number
,因此您始终知道会发生什么。 Integer
扩展Integer
后,Number
可以添加到此列表中。但是,您无法将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
。
我希望这可以解决问题。
答案 1 :(得分:4)
这将解释不同之处:
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 {}
}
如您所见,调用方法并分配变量/实例字段具有相同的规则。查看方法调用,将其参数赋值给其声明的参数。
答案 2 :(得分:1)
ArrayList<A>
表示特定的A类,其中ArrayList<? extends A>
表示A类或任何使用A(Sub的A类)的类,这使得它更通用
答案 3 :(得分:1)
使用private ArrayList<A> aList;
作为变量声明并不等同于使用通配符private ArrayList<? extends A> aList;
通配版本允许您分配任何类型的ArrayLists,这些类型扩展A和A本身但拒绝向列表中添加元素,因为它无法确定它是否是类型安全的。另一方面,使用ArrayList<A>
,您只能分配类型A的ArrayLists(或ArrayList的扩展名),然后您可以添加A元素和任何扩展A的元素。
仅供参考:您应该更喜欢使用更抽象的类型来声明变量/参数,例如List<A>
或Collection<A>
。
答案 4 :(得分:0)
一开始很难掌握,但继承不适用于泛型,即如果B扩展A,List<B>
不是“子类”(不能分配) to)List<A>
。此外,List<? extends A>
不是(不能分配给)List<A>
的“子类”。
答案 5 :(得分:0)
主要区别在于,如果泛型表单在基类(或接口)中的方法中用作参数或返回类型,则允许更大范围的类型签名计为覆盖而不是重载。
Function override-overload in Java
例如,以下代码是合法的(在Java 7中):
interface A
{
List<? extends Number> getSomeNumbers();
}
class B implements A
{
@Override
public ArrayList<Integer> getSomeNumbers()
{
return new ArrayList<>();
}
}
Difference between Enumeration<? extends ZipEntry> and Enumeration<ZipEntry>?
所有这一切都意味着有时你可以编写一些代码,当其他人使用它时需要更少的演员表。这不仅应该减少他们必须做的打字,还要消除可能的失败。
当然,Java泛型的问题在于它们是以受向后兼容性约束的方式引入的。因此,并非一切都是您认为应该的,并且细节变得非常毛茸茸的确切有效和无效。
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#FAQ812