使用Java5我们可以写:
Foo[] foos = ...
for (Foo foo : foos)
或者只是在for循环中使用Iterable。这非常方便。
但是你不能像这样编写一个可迭代的泛型方法:
public void bar(Iterable<Foo> foos) { .. }
并使用数组调用它,因为它不是Iterable:
Foo[] foos = { .. };
bar(foos); // compile time error
我想知道这个设计决定背后的原因。
答案 0 :(得分:70)
数组可以实现接口(Cloneable
和java.io.Serializable
)。那么为什么不Iterable
?我猜Iterable
强制添加iterator
方法,而数组不实现方法。 char[]
甚至不会覆盖toString
。无论如何,引用数组应该被认为不太理想 - 使用List
s。正如dfa评论,Arrays.asList
将明确地为您进行转换。
(话虽如此,你可以在数组上调用clone
。)
答案 1 :(得分:55)
数组是一个Object,但它的项可能不是。该数组可能包含像int这样的基本类型,Iterable无法应对。至少那是我认为的。
答案 2 :(得分: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 };
所有仍然不是很完美,因为可变列表界面IList<T>
不会继承IReadOnlyList<T>
:
IList<int> c = new List<int> { 1, 2, 3, 4 };
IReadOnlyList<int> d = c; // error
也许这种变化可能会产生向后兼容性。
如果在较新版本的Java中类似的东西有任何进展,我有兴趣在评论中知道! :)
答案 3 :(得分:14)
不幸的是,数组不是“class
- 足够”。它们没有实现Iterable
接口。
虽然数组现在是实现Clonable和Serializable的对象,但我相信数组不是正常意义上的对象,并且不实现接口。
你可以在for-each循环中使用它们的原因是因为Sun在数组中添加了一些合成糖(这是一个特例)。
由于数组最初是用Java 1创建的“几乎对象”,因此在Java中使用真正的对象进行更改将非常激烈。
答案 4 :(得分:2)
编译器实际上将数组上的for each
转换为带有计数器变量的简单for
循环。
编译以下内容
public void doArrayForEach() {
int[] ints = new int[5];
for(int i : ints) {
System.out.println(i);
}
}
然后反编译.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);
}
}