繁体   English   中英

“ArrayList <A>”和“ArrayList”</a>之间的实际区别是什么? <? extends A> <A>“?</a>

[英]What is the practical difference between “ArrayList<A>” and “ArrayList<? extends A>”?

我看了问题q1q2q3 ,但它们并没有完全涵盖我的问题。

注意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在该列表以及任何子NumberIntegerLong

顺便说一下,有一条规则说生产者延伸,消费者超级 (简称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中的函数覆盖 - 重载

例如,以下代码是合法的(在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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM