(1) List<?> myList = new ArrayList<?>();

(2) ArrayList<?> myList = new ArrayList<?>();

我知道,对于(1),List接口的实现可以交换。似乎(1)通常在应用程序中使用,无论是否需要(我自己总是使用它)。

我想知道是否有人使用(2)?

此外,情况实际上需要使用(1)而不是(2)的频率是多少(请给我一个例子)(即(2)是不够的..除了编码到接口和最佳实践等)。


当前回答

出以下两种:

(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();

第一种通常是首选。因为你将只使用List接口的方法,它为你提供了使用其他List实现的自由,例如未来的LinkedList。所以它将你与特定的实现分离。现在有两点值得一提:

我们应该始终按照接口编程。更多的在这里。 你几乎总是会使用数组列表而不是LinkedList。更多的在这里。

我想知道是否有人使用(2)

有时会(很少阅读)。当我们需要的方法是ArrayList实现的一部分而不是接口List的一部分时。例如ensureCapacity。

还有,这种情况发生的频率(以及我能得到一个例子吗) 实际上需要使用(1)而不是(2)

这是OOP中的一个经典设计模式,你总是试图将你的代码从特定的实现和程序解耦到接口。

其他回答

如果code是列表的“所有者”,我使用(2)。例如,对于局部变量就是如此。没有理由使用抽象类型List而不是ArrayList。 另一个展示所有权的例子:

public class Test {

    // This object is the owner of strings, so use the concrete type.
    private final ArrayList<String> strings = new ArrayList<>();

    // This object uses the argument but doesn't own it, so use abstract type.
    public void addStrings(List<String> add) {
        strings.addAll(add);
    }

    // Here we return the list but we do not give ownership away, so use abstract type. This also allows to create optionally an unmodifiable list.
    public List<String> getStrings() {
        return Collections.unmodifiableList(strings);
    }

    // Here we create a new list and give ownership to the caller. Use concrete type.
    public ArrayList<String> getStringsCopy() {
        return new ArrayList<>(strings);
    }
}

我认为使用(2)的人不知道利斯科夫替换原则或依赖反转原则。或者必须使用数组列表。

当你写List时,你实际上告诉,你的对象只实现了List接口,但你没有指定你的对象属于什么类。

在编写ArrayList时,指定对象类是一个可调整大小的数组。

因此,第一个版本使您的代码将来更加灵活。

看看Java文档:

类ArrayList——List接口的可调整大小的数组实现。

接口列表-一个有序的集合(也称为序列)。该接口的用户可以精确控制每个元素在列表中的插入位置。

数组—容器对象,保存固定数量的单一类型的值。

有人又问了这个问题(重复),这让我在这个问题上更深入了一些。

public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    list.add("a");
    list.add("b");

    ArrayList<String> aList = new ArrayList<String>();
    aList.add("a");
    aList.add("b");

}

如果我们使用字节码查看器(我使用http://asm.ow2.org/eclipse/index.html) weĺl看到以下(仅列表初始化和赋值)为我们的列表片段:

   L0
    LINENUMBER 9 L0
    NEW ArrayList
    DUP
    INVOKESPECIAL ArrayList.<init> () : void
    ASTORE 1
   L1
    LINENUMBER 10 L1
    ALOAD 1: list
    LDC "a"
    INVOKEINTERFACE List.add (Object) : boolean
    POP
   L2
    LINENUMBER 11 L2
    ALOAD 1: list
    LDC "b"
    INVOKEINTERFACE List.add (Object) : boolean
    POP

对于alist来说:

   L3
    LINENUMBER 13 L3
    NEW java/util/ArrayList
    DUP
    INVOKESPECIAL java/util/ArrayList.<init> ()V
    ASTORE 2
   L4
    LINENUMBER 14 L4
    ALOAD 2
    LDC "a"
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP
   L5
    LINENUMBER 15 L5
    ALOAD 2
    LDC "b"
    INVOKEVIRTUAL java/util/ArrayList.add (Ljava/lang/Object;)Z
    POP

区别在于list调用INVOKEINTERFACE,而aList调用invokvirtual。根据Bycode大纲插件参考,

invokeinterface用于调用Java中声明的方法 接口

虽然invokevirtual

调用除接口方法(使用 Invokeinterface)、静态方法(使用invokestatic)等等 特殊情况由invokspecial处理。

总之,invokvirtual从栈中弹出objectref,而对于invokeinterface

解释器从操作数堆栈中取出'n'项,其中'n'是8位无符号 从字节码获取的整数参数。第一项是 Objectref,对正在调用其方法的对象的引用。

如果我理解正确的话,区别基本上是每种方法检索objectref的方式。

出以下两种:

(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();

第一种通常是首选。因为你将只使用List接口的方法,它为你提供了使用其他List实现的自由,例如未来的LinkedList。所以它将你与特定的实现分离。现在有两点值得一提:

我们应该始终按照接口编程。更多的在这里。 你几乎总是会使用数组列表而不是LinkedList。更多的在这里。

我想知道是否有人使用(2)

有时会(很少阅读)。当我们需要的方法是ArrayList实现的一部分而不是接口List的一部分时。例如ensureCapacity。

还有,这种情况发生的频率(以及我能得到一个例子吗) 实际上需要使用(1)而不是(2)

这是OOP中的一个经典设计模式,你总是试图将你的代码从特定的实现和程序解耦到接口。