Есть ли параметр, который я могу использовать в Java, который работает со всеми циклами for-each? - PullRequest
7 голосов
/ 20 апреля 2009

Предположим, у меня есть метод, который принимает массив и обрабатывает каждый элемент в нем, используя встроенный в Java цикл for-each , например:

public static void myFun(SomeClass[] arr) {
    for (SomeClass sc : arr) {
        // Stuff is processed here
    }
}

Это работает просто отлично, но теперь я хочу иметь возможность передать тот же метод List<SomeClass>. Мне суждено использовать Collection.toArray (T []) или есть параметр, который я могу использовать для myFun(), который принимает любой тип, который может использоваться в конструкции for-each? *

Чтобы уточнить: я хочу подпись метода, которая будет принимать любой итерируемый объект, будь то примитивный массив или коллекция. Я очень легко могу написать два метода, один из которых оборачивает другой, но мне просто интересно, есть ли лучший способ.

Ответы [ 6 ]

9 голосов
/ 20 апреля 2009

Я бы предложил использовать Iterable, Collection или List в качестве типа параметра.

ИМО, коллекции следует отдавать предпочтение ссылочным массивам. Если у вас есть массив 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 голосов
/ 20 апреля 2009

Использование Iterable. Вот для чего.

Как вы сказали, Iterable не будет обрабатывать массивы.

Вы не хотите использовать несколько методов, оборачивающих друг друга. Это исключает Arrays.asList и Collection.toArray.

Так что ответ на ваш вопрос - нет, пути нет. Но если вы можете использовать Lists, зачем вам использовать массивы?

Я бы все равно пошел с Iterable здесь. Мне нравится это лучше, чем Collection, потому что в прошлом у меня были классы, которые реализовали Iterable, но не были коллекциями; это облегчало им ленивый поиск значений по мере необходимости, и я мог использовать цикл foreach для них.

3 голосов
/ 20 апреля 2009

вы не можете , Java-массивы не реализует итерируемые:

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 (Collections) и / или Array, хотя бы один метод должен выполнить некоторое преобразование (в / из массива)

* 1009 Временное решение *:

вы можете попробовать с адаптером:

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 голос
/ 20 апреля 2009

Краткий ответ: нет, не существует единственной сигнатуры метода, которая бы безошибочно принимала тип 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);
    }
}

Хотя источник этих двух методов выглядит совершенно одинаково, вам нужно будет определить его дважды (если, конечно, вы не заключите массив в свой собственный Iterable, как обрисовано в общих чертах @dfa).

0 голосов
/ 20 апреля 2009

В Java 1.5+ есть небольшая известная особенность Java Generics, где вы можете использовать <? extends Subtype> в вызовах методов и конструкторах. Вы можете использовать <? extends 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>, где она принимает параметр, который является супертипом данного типа, а не подтипом. Есть некоторые ограничения на то, для чего вы можете использовать «расширяет» и «супер» таким образом. Вот хорошее место, чтобы найти больше информации .

0 голосов
/ 20 апреля 2009
public static void myFun(Collection<MyClass> collection) {
    for (MyClass mc : collection) {
        // Stuff is processed here
    }
}
...