35

implements和 extends的java.util.ArrayList实现。但是在 java 文档中你可以看到 AbstractList 已经实现了 List。那么实现List和扩展AbstractList不是多余的吗? 我的第二个问题 请看下面的代码:ListAbstractList



String str = "1,2,3,4,5,6,7,8,9,10";
String[] stra = str.split(",");
List<String> a = Arrays.asList(stra);

Arrays 类的Arrays.asList()方法包含它自己的 ArrayList 实现。但是这个只是扩展了 AbstractList 而没有实现 List。但是上面的代码可以编译。
但是当代码修改为以下

String str = "1,2,3,4,5,6,7,8,9,10";
String[] stra = str.split(",");
java.util.ArrayList<String> a = Arrays.asList(stra);

我收到一个错误:cannot convert form List<String> to ArrayList<String>
这背后的原因是什么?
EDIT
Arrays.asList()确实返回了它自己的 ArrayList 实现。看看这个

4

8 回答 8

29

那么实现List和扩展AbstractList不是多余的吗?

是的,它是 100% 冗余的。然而,Java 实现者在集合库的所有公共实现中非常一致地添加了接口:

  • LinkedList<E>ArrayList<E>扩展AbstractList<E>which implements List<E>,然后实现List<E>它们自己。
  • HashSet<E>TreeSet<E>扩展AbstractSet<E>which implements Set<E>,然后实现Set<E>它们自己。
  • HashMap<K,V>TreeMap<E>扩展AbstractMap<K,V>which implements Map<K,V>,然后实现Map<K,V>它们自己。

我的理解是他们这样做是出于文档目的:作者想表明这ArrayList<E>主要是一个List<E>; ArrayList<E>扩展的事实AbstractList<E>是其实现的一个不太重要的细节。其他公共集合类型也是如此。

请注意,Arrays.ArrayList<E>该类不是公开可见的,因此其作者并不关心List<T>明确包含。

就失败的转换而言,这不足为奇,因为内部类Arrays.ArrayList<E>和公共类ArrayList<E>彼此无关。

于 2013-09-01T12:57:55.450 回答
9

对于您的第一个问题,请查看为什么 ArrayList 有“实现列表”?


回答你的第二个问题

java.util.ArrayList<String> a = Arrays.asList(stra);

正如你所提到的,Arrays.asList它返回了它自己的 AbstractList 实现,不幸的是,这段代码的创建者也将这个类命名为 ArrayList。现在因为我们不能水平转换但只能垂直返回数组列表不能转换为java.utli.ArrayList但只能转换为java.util.AbstractList或其超类型java.util.List,这就是您的第一个代码示例有效的原因。

于 2013-09-01T13:08:37.170 回答
4

Arrays.asList返回一个List。因此,将其转换为ArrayList是不安全的,因为您不知道返回的List类型(取决于它创建列表的数组类型)。您的第二个片段隐式需要一个ArrayList 。因此,当您的第一个代码段编译良好时它会失败,因为它需要一个List。你可以做-

ArrayList<String> a = new ArrayList<String>(Arrays.asList(stra));
于 2013-09-01T12:45:48.470 回答
3

我相信,是有原因的。这只是我的想法,我在 JLS 的任何地方都找不到。

如果我是一名开发人员,正在编写一个将被广泛使用的 API,我为什么要这样做?

绝对没有这样做的理由,但考虑一下这种情况,我已经编写了List接口并提供了接口的ArrayList实现List

到目前为止,我还没有编写任何抽象类AbstractList

有一天,一个需求来了,我被要求编写更多接口的实现,List其中大多数接口中的方法具有相似或相同的具体abstract方法List

我将继续AbstractList为所有这些方法编写一个必要的实现。但现在我不喜欢我的一半类实现List接口,一半扩展AbstractList

此外,我不能直接从我之前编写的类中删除“实现列表”,可能是因为现在不是正确的时间,或者我不希望其他人的代码在我的新版本中中断。

注意这只是我的意见。

于 2013-09-01T14:46:44.513 回答
3

您的第一个问题的答案是实施 List 是一个合同。该合约可以由 AbstractList 和 ArrayList 定义。ArrayList 实现 List 以发布在将来可能需要不从可能实现也可能不实现 List 的 AbstractList 扩展时尊重 List 契约的事实。

对于第二个问题:Arrays.asList 返回一个 List。在当前实现中可能会返回 ArrayList。例如,在下一个版本中,可能会返回一个不同的列表 LinkedList,并且合同(由方法签名定义)仍将受到尊重。

于 2013-09-01T13:09:46.173 回答
2

1)ArrayList implements List是多余的,但仍然合法。只有 JCF(Java 集合框架)设计者才能回答为什么。由于首席 JCF 设计师 J.Bloch 在“Effective Java”中没有说明为什么会这样,看来我们永远不会知道为什么。

2) Arrays.asList 返回

public class Arrays {
   ...

    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
...

它不是 java.util.ArrayList 并且不能转换为它

于 2013-09-01T13:04:43.710 回答
1

我的回答会简单直接。

实现List和扩展AbstractList不是多余的吗?

是的,但他们这样做只是为了澄清代码,以便容易看到该类实现了 List 接口。

Arrays 类的 Arrays.asList() 方法包含它自己的 ArrayList 实现。但是这个只是扩展了 AbstractList 而没有实现 List。

如您所见,这是多余的,如果 AbstractList 已经声明了该实现,则无需重新声明 List 接口的实现。

我收到一个错误:无法将表单列表转换为 ArrayList 这背后的原因是什么?

Arrays.asList() 返回一个 List,它可以是任何类型的 List。该代码中实现的 ArrayList 与 java.util.ArrayList 的 ArrayList 不同,它们只是共享相同的名称,但它们不是相同的代码。

于 2013-09-01T15:41:38.603 回答
1

只是想补充问题2的答案

java.util.ArrayList<String> a=Arrays.asList(stra);

编译器只知道Arrays.asListis的返回类型List,但不知道它的确切实现可能不是java.util.ArrayList。所以你得到了这个编译时错误。

类型不匹配:无法从 List 转换为 ArrayList

你可以像这样显式地强制上演员,

java.util.ArrayList<String> a =(java.util.ArrayList<String>)Arrays.asList(stra);

代码会编译成功,但是会发生运行时异常,

java.lang.ClassCastException:java.util.Arrays$ArrayList无法转换为java.util.ArrayList

这是因为java.util.Arrays$ArrayListArrays.asList返回的实现类型)不是java.util.ArrayList.

于 2018-02-22T09:47:21.693 回答