假设我有一个方法,它接受一个数组并使用 Java 内置的 for-each 循环处理其中的每个元素,如下所示:
public static void myFun(SomeClass[] arr) {
for (SomeClass sc : arr) {
// Stuff is processed here
}
}
这工作得很好,但现在我希望能够传递相同的方法a
List<SomeClass>
。我注定要使用 Collection.toArray(T []),还是有一个参数可以用于 myFun()
,它接受可以在 for-each 构造中使用的任何类型?
澄清一下:我想要一个接受任何可迭代对象的方法签名,无论是原始数组还是集合。我可以很容易地编写两种方法,一种方法包装另一种方法,但我只是好奇是否有更好的方法。
我建议使用
Iterable
、Collection
或 List
作为参数类型。
IMO,集合应该优先于引用数组。如果你碰巧有一个数组
Arrays.asList
可以很好地进行转换。 Arrays.asList
允许获取和设置回数组,但显然不允许“结构”修改,这会改变数组长度。
myFun(Arrays.asList(arr));
在极端/一般情况下,您可能必须使用通配符。
public static void myFun(Iterable<? extends SomeClass> somethings) {
for (SomeClass something : somethings) {
// something is processed here
}
}
值得注意的是
Collections.toArray
和 Arrays.asList
的工作方式略有不同。 asList
保留原始数组来支持集合,因此对集合的更改将反映在数组中。 Collections.toArray
制作集合数据的(浅)副本。如果您返回一个数组,那么制作一个副本通常是您想要的。不对称地,如果您作为参数传递,您通常不会复制(除非存储为字段)。
你不能,java Arrays 没有实现 Iterable:
public static int sum(Iterable<Integer> elements) {
int s = 0;
for (int i : elements) {
s += i;
}
return s;
}
public static void main(String[] args) {
L1: System.out.println(sum(1,2,3));
L2: System.out.println(sum(Arrays.asList(1,2,3)));
L3: System.out.println(sum(new int[] { 1,2,3 }));
}
这会导致(L1 和 L3)中出现两个编译时错误;所以你必须设计你的 接受 Iterable(集合)和/或数组的方法,至少有一个方法必须执行某种转换(到/从数组)
解决方法:
您可以尝试使用适配器:
public class ArrayIterator<T> implements Iterator<T> {
private final T[] array;
private int i;
public ArrayIterator(T[] anArray) {
array = anArray;
i = 0;
}
public boolean hasNext() {
return i < array.length;
}
public T next() {
return array[i++];
}
public void remove() {
throw new UnsupportedOperationException();
}
}
private static int sum(final Integer ... elements) {
return sum(new Iterable<Integer>() {
public Iterator<Integer> iterator() {
return new ArrayIterator<Integer>(elements);
}
});
}
只有在处理原始数组时才应该注意;当你只使用 引用对象(你的情况)ArrayIterator + 匿名类很酷
希望有帮助
简短的回答:不,没有一个方法签名可以类型安全地接受 Iterable 和数组。显然你可以接受
Object
,但这也是一种黑客行为。
长答案:由于增强的
for
循环实际上定义了两次(一次用于数组,一次用于 Iterable
),因此您还需要提供两个重载方法:
public static void myFun(SomeClass[] array) {
for (SomeClass sc : array) {
doTheProcessing(sc);
}
}
public static void myFun(Iterable<? extends SomeClass> iterable) {
for (SomeClass sc : iterable) {
doTheProcessing(sc);
}
}
虽然这两个方法的源代码看起来完全相同,但您需要定义两次(当然,除非您按照 @dfa 的概述将数组包装在自己的
Iterable
中)。
Java 1.5+ 中的 Java 泛型有一个鲜为人知的功能,您可以在方法调用和构造函数中使用
<? extends Subtype>
。您可以使用 <? extends Object>
,然后任何处理这些的内容都只能访问 Object 上的方法。您可能真正想要的是更像这样的东西:
List<? extends MyCrustaceans> seaLife = new ArrayList<? extends MyCrustaceans>();
MyShrimp s = new MyShrimp("bubba");
seaLife.add(s);
DoStuff(seaLife);
...
public static void DoStuff(List<? extends MyCrustaceans> seaLife)
{
for (MyCrustaceans c : seaLife) {
System.out.println(c);
}
}
因此,如果您有一个基类(如 MyCrustaceans),您可以在 DoStuff 中使用该基类的任何方法(或者 Object 类的方法,如果您的参数是
<? extends Object>
)。 <? super MyType>
有一个类似的功能,它接受给定类型的超类型而不是子类型的参数。以这种方式使用“extends”和“super”的用途有一些限制。这是一个查找更多信息的好地方。
public static void myFun(Iterable<?> iterable){
for(Object o:iterable){
// System.out.println(o);
// Process it here.
}
}
这个方法应该适合你。它接受一个可迭代对象作为参数。这 ?通配符意味着该方法可以接受任何类型的可迭代对象。
public static void myFun(Collection<MyClass> collection) {
for (MyClass mc : collection) {
// Stuff is processed here
}
}