Dan Lew
Dan Lew

Reputation: 87430

Is there a parameter I can use in Java that works with all for-each loops?

Suppose I've got a method that accepts an array and processes each element in it using Java's built in for-each loop, like this:

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

This works just fine, but now I want to be able to pass the same method a List<SomeClass> instead. Am I destined to use Collection.toArray(T []), or is there a parameter I can use for myFun() that accepts any type that can be used in a for-each construct?

To clarify: I want a method signature that will accept any iterable object, be it a primitive array or a Collection. I can very easily write two methods, with one wrapping the other, but I'm just curious if there's a better way.

Upvotes: 8

Views: 3861

Answers (7)

Sirish Vasista
Sirish Vasista

Reputation: 1

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

This method should work for you. It takes an iterable object as a parameter. The ? wildcard character means that the method can accept any type of iterable object.

Upvotes: 0

Tom Hawtin - tackline
Tom Hawtin - tackline

Reputation: 147154

I would suggest using Iterable, Collection or List as the parameter type.

IMO, collections should be preferred to reference arrays. If you happen to have an array Arrays.asList does the conversion nicely. Arrays.asList allows gets and sets back through to the array, but obviously not "structural" modifications which would change the array length.

myFun(Arrays.asList(arr));

You may have to use wildcards in extreme/general cases.

public static void myFun(Iterable<? extends SomeClass> somethings) {
    for (SomeClass something : somethings) {
        // something is processed here
    }
}

It is noteworthy that Collections.toArray and Arrays.asList work slightly differently. asList keeps the original array to back the collection, so changes to the collection will be reflected in the array. Collections.toArray makes a (shallow) copy of the collection data. Making a copy is often what you would want anyway if you are returning an array. Asymmetrically, if you are passing as an argument you generally do not copy (unless storing as a field).

Upvotes: 10

Joachim Sauer
Joachim Sauer

Reputation: 308021

Short answer: no, there's no single method signature that type-safely accepts both an Iterable and an array. Obviously you could just accept Object, but that would be a hack as well.

Long-ish answer: Since the enhanced for-loop is effectively defined twice (once for arrays and once for Iterable), you'll need to provide two overloaded methods as well:

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);
    }
}

Although the source of the two methods looks exactly the same, you'll need to define it twice (unless of course you wrap the Array in your own Iterable as @dfa outlined).

Upvotes: 1

Ogre Psalm33
Ogre Psalm33

Reputation: 21946

There's a little know feature of Java Generics in Java 1.5+ where you can use <? extends Subtype> in your method calls and constructors. You could use <? extends Object>, and then anything that deals with those would have access only to methods on Object. What you might really want is something more like this:

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);
    }
}

So if you have a base class (like MyCrustaceans), you can use any methods of that base class in DoStuff (or of the Object class, if your parameter is <? extends Object>). There's a similar feature of <? super MyType>, where it accepts a parameter that is a supertype of the given type, instead of a subtype. There's some restrictions on what you can use "extends" and "super" for in this fashion. Here's a good place to find more info.

Upvotes: 0

dfa
dfa

Reputation: 116334

you cannot, java Arrays doesn't implements 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 }));
}

this results in two compile-time errors in (L1 and L3); so you must design your method to accept an Iterable (Collections) and/or an Array, at least one method must perform some conversion (to/from array)

WORKAROUND:

you may be try with an adapter:

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);
        }
    });
}

you should pay attention only when dealing with primitive arrays; when you use only reference object (your case) ArrayIterator + anonymous class are cool

hope it helps

Upvotes: 3

Michael Myers
Michael Myers

Reputation: 191895

Use Iterable. That's what it's for.

As you said, Iterable won't handle arrays.

You don't want to use multiple methods wrapping each other. That rules out Arrays.asList and Collection.toArray.

So the answer to your question is no, there isn't a way. But if you can use Lists, why would you ever use arrays?

I would still go with Iterable here. I like it better than Collection because in the past I've had classes that implemented Iterable but were not collections; this made it easy for them to lazily retrieve values as needed, and I could use the foreach loop on them.

Upvotes: 5

Dima
Dima

Reputation: 4128

public static void myFun(Collection<MyClass> collection) {
    for (MyClass mc : collection) {
        // Stuff is processed here
    }
}

Upvotes: -3

Related Questions