我可以在 Java 中使用适用于所有 for-each 循环的参数吗?

问题描述 投票:0回答:7

假设我有一个方法,它接受一个数组并使用 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 构造中使用的任何类型?

澄清一下:我想要一个接受任何可迭代对象的方法签名,无论是原始数组还是集合。我可以很容易地编写两种方法,一种方法包装另一种方法,但我只是好奇是否有更好的方法。

java iterator foreach
7个回答
10
投票

我建议使用

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
制作集合数据的(浅)副本。如果您返回一个数组,那么制作一个副本通常是您想要的。不对称地,如果您作为参数传递,您通常不会复制(除非存储为字段)。


5
投票

使用

Iterable
。这就是它的用途。

正如您所说,

Iterable
不会处理数组。

您不想使用多个相互包装的方法。这就排除了

Arrays.asList
Collection.toArray

所以你的问题的答案是否定的,没有办法。但如果你可以使用

Lists
,为什么还要使用数组呢?

我仍然会选择

Iterable
。我比
Collection
更喜欢它,因为过去我有一些类实现了
Iterable
但不是集合;这使他们可以轻松地根据需要延迟检索值,并且我可以对它们使用 foreach 循环。


3
投票

你不能,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 + 匿名类很酷

希望有帮助


1
投票

简短的回答:不,没有一个方法签名可以类型安全地接受 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
中)。


0
投票

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”的用途有一些限制。这是一个查找更多信息的好地方


0
投票
public static void myFun(Iterable<?> iterable){
    for(Object o:iterable){
        // System.out.println(o);
        // Process it here. 
    }
}

这个方法应该适合你。它接受一个可迭代对象作为参数。这 ?通配符意味着该方法可以接受任何类型的可迭代对象。


-3
投票
public static void myFun(Collection<MyClass> collection) {
    for (MyClass mc : collection) {
        // Stuff is processed here
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.