为什么数组不能分配给Iterable?

dfa*_*dfa 176 java language-design

用Java5我们可以写:

Foo[] foos = ...
for (Foo foo : foos) 
Run Code Online (Sandbox Code Playgroud)

或者只是在for循环中使用Iterable.这非常方便.

但是你不能像这样为iterable编写泛型方法:

public void bar(Iterable<Foo> foos) { .. }
Run Code Online (Sandbox Code Playgroud)

并使用数组调用它,因为它不是Iterable:

Foo[] foos = { .. };
bar(foos);  // compile time error 
Run Code Online (Sandbox Code Playgroud)

我想知道这个设计决定背后的原因.

Tom*_*ine 73

数组可以实现接口(Cloneablejava.io.Serializable).那为什么不Iterable呢?我想Iterable强制添加一个iterator方法,并且数组不实现方法.char[]甚至没有覆盖toString.无论如何,引用数组应该被认为不太理想 - 使用Lists.正如dfa评论,Arrays.asList将明确地为您进行转换.

(话虽如此,你可以调用clone数组.)

  • >"......并且数组不实现方法." 我认为这是另一个哲学问题; 数组不是原始类型,Java哲学读取"一切都是对象(原始类型除外)".那么,为什么数组不实现方法,即使从一开始就有人想要使用数组的大量操作.哦,没错,在仿制药出现之前,阵列是唯一的强类型集合,这是一个令人遗憾的后见之明. (21认同)
  • 如果你有一个数组中的数据,那么你可能正在进行低级的,性能关键的工作,比如处理byte []从流中读取.无法迭代数组可能源于Java泛型不支持原语作为类型参数,正如@Gareth在下面所述. (2认同)
  • @FatuHoku暗示泛型是一个遗憾,事后看来是不正确的。泛型的可取性一直受到赞赏。数组不是原语(我不是说它们是原语),但是它们是低级的。您想对数组做的一件事是将它们用作类似矢量的结构的实现细节。 (2认同)
  • `Iterator&lt;T&gt;` 也需要 `remove(T)`,尽管它可以抛出 `UnsupportedOperationException`。 (2认同)

小智 57

该数组是一个Object,但它的项可能不是.该数组可能包含像int这样的基本类型,Iterable无法应对.至少那是我认为的.

  • 这不会阻止Object数组实现Iterable.它也不会阻止原始数组为包装类型实现Iterable. (7认同)
  • 这意味着为了支持`Iterable`接口,原始数组必须专门用于使用包装类.但这并不是什么大不了的事,因为无论如何类型参数都是假的. (3认同)
  • 自动装箱可以解决这个问题 (2认同)

Dan*_*ker 16

数组应该支持Iterable,它们只是不支持,出于同样的原因,.NET数组不支持允许按位置进行只读随机访问的接口(没有这样的接口被定义为标准).基本上,框架通常会有一些令人烦恼的小差距,这是不值得任何人花时间修复的.如果我们能够以某种最佳方式自己修复它们并不重要,但通常我们不能.

更新:为了公平,我提到.NET数组不支持按位置支持随机访问的接口(另请参阅我的评论).但是在.NET 4.5中,已经定义了精确的接口,并且数组和List<T>类支持:

IReadOnlyList<int> a = new[] {1, 2, 3, 4};
IReadOnlyList<int> b = new List<int> { 1, 2, 3, 4 };
Run Code Online (Sandbox Code Playgroud)

所有仍然不是很完美,因为可变列表接口IList<T>不继承IReadOnlyList<T>:

IList<int> c = new List<int> { 1, 2, 3, 4 };
IReadOnlyList<int> d = c; // error
Run Code Online (Sandbox Code Playgroud)

也许这种变化可能会产生向后兼容性.

如果在较新版本的Java中类似的东西有任何进展,我会有兴趣在评论中知道!:)

  • .NET数组实现IList接口 (8认同)
  • @Aphid - 我说*readonly*随机访问.`IList <T>`公开修改操作.如果`IList <T>`继承了类似`IReadonlyList <T>`接口的东西,那将是很好的,它只有`Count`和`T this [int]`并且继承了`IEnumerable <T>`(已经支持readonly枚举).另一个好处是用于获取逆序枚举器的接口,`Reverse`扩展方法可以查询(就像`Count`扩展方法查询`ICollection`以优化自身一样.) (2认同)
  • .NET 中的数组从 .NET 1.1 开始实现“IList”和“ICollection”,从 .NET 2.0 开始实现“IList&lt;T&gt;”和“ICollection&lt;T&gt;”。这是 Java 远远落后于竞争对手的又一个例子。 (2认同)

jjn*_*guy 14

不幸的是,数组不是" class足够".他们没有实现Iterable接口.

虽然数组现在是实现Clonable和Serializable的对象,但我相信数组不是正常意义上的对象,并且不实现接口.

你可以在for-each循环中使用它们的原因是因为Sun在数组中添加了一些合成糖(这是一个特例).

由于数组最初是用Java 1开始的"几乎对象",因此在Java中使用它们成为真正的对象时,它的变化太大了.

  • java数组是所有意义上的对象.请删除那一点错误信息.他们只是没有实现Iterable. (34认同)
  • 尽管如此,每个循环都有糖,所以为什么不能为Iterable提供糖? (14认同)
  • 数组*可以*实现接口.它们实现了`Cloneable`和`Serializable`接口. (12认同)
  • @mmyers:for-each中使用的糖是*编译时*糖.这比*VM*糖要容易得多.已经说过,.NET阵列在这个领域明显更好...... (8认同)
  • 数组是一个Object.它支持*有用*:P方法,如wait(),wait(n),wait(n,m),notify(),notifyAll(),finalize(),toString()的无意义实现唯一有用的方法是getClass (). (5认同)
  • 他们可以轻松地将标准库中的数组包装成带有方法的东西,为后面的compat保留旧的`Object []`语法.事情会变得更方便.每当我遇到自己使用java时,我觉得我的双手并列.我很高兴scala在stdlib中有这个,最后收藏品也加入了`join` ... (3认同)
  • 他们也可以完成Iterable sugar编译时间.如果你将一个数组传递给想要Iterable的东西,它*可以*为你放置Arrays.asList()... (2认同)

das*_*eks 5

编译器实际上将数组上的 转换为带有计数器变量的for each简单循环。for

编译以下内容

public void doArrayForEach() {
    int[] ints = new int[5];

    for(int i : ints) {
        System.out.println(i);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后反编译 .class 文件产生

public void doArrayForEach() {
    int[] ints = new int[5];
    int[] var2 = ints;
    int var3 = ints.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        int i = var2[var4];
        System.out.println(i);
    }
}
Run Code Online (Sandbox Code Playgroud)