letting a java function accept a collection or an array
Asked Answered
V

3

14

I am trying to write a function that takes some strings and does something with them.

The only thing I'm going to do that the set of strings is loop over them. Right now I end up with an awkward construct along the lines of

public void foo(String[] myStrings){
    foo(java.util.Arrays.asList(myStrings));
}

public void foo(Iterable<String> myStrings){
    for(String i : myStrings){
        bar(i);
    }
}

which feels redundant since

for(String i : myStrings){
    bar(i);
}

would be perfectly valid code for myStrings of type String[].

Is there a class that I can have foo accept which will allow both collections and arrays?

Vulgate answered 10/12, 2012 at 18:45 Comment(0)
P
13

See Why is an array not assignable to Iterable?

Short answer: no. Array types are synthetic code, as I understand it, and thus do not implement Iterable or any other types. You need to provide an overloaded method or require clients to call Arrays.asList at the call site.

Primitive answered 10/12, 2012 at 18:48 Comment(3)
Thanks, I think I was still trying to convince java that deep in its heart it was python.Vulgate
Haha, no duck typing here -- sorry. :)Primitive
Welcome to the world of verbose typing. Java's based on the principle of WET rather than DRY (as in Write Everything Twice rather than Dont Repeat Yourself).Postage
F
3

Unfortunately array does not implement Iterable, despite the for-each loop working on both.

You could exclusively only accept Iterable, as passing around arrays is a bit old school (the performance benefits of arrays over ArrayList is negligible). An existing array can be easily wrapped and converting to a List using Arrays.asList(T... a).

Forethoughtful answered 10/12, 2012 at 18:48 Comment(1)
That is a good call. I use ArrayLists in any new code that I write, but there are a lot of arrays floating around that I've inherited.Vulgate
Q
3

Although arrays can be used in a for-each loop, they do not implement Iterable. There are simply two possibilities, either overload the method as you've stated, or just provide only the iterable variant and force the client to call Arrays.asList().

In case you want to also provide the array overload, you might change its signature from simple array to varargs:

public void foo(String... myStrings){
    foo(java.util.Arrays.asList(myStrings));
}

In such case, beware of the incompatibility between primitive arrays and primitive-wrapper arrays:

static void foo(int... ints) {}
foo(new Integer[] {1, 2}); // compile error

and:

static void foo(Integer... integers) {}
foo(new int[] { 1, 2 }); // compile error

And the most obscure part, with generic varargs:

static <T> T[] foo(T... ts) {
    return ts;
}

If you pass an array of Integers:

Integer[] integers = { 1, 2 };
System.out.println(Arrays.deepToString(foo(integers)));
> [1, 2]

The value of ts is an array of Integers with 2 elements: 1 and 2.

However, if you pass an array of primitive ints, a funny thing happens:

int[] ints = { 1, 2 };
System.out.println(Arrays.deepToString(foo(ints)));
> [[1, 2]]

In this case, the value of ts is an array of int arrays (int[][]) with only one element, which is the originally passed array. The reason for this is that an int isn't an Object (autoboxing doesn't help here), while an array of ints is, so the value of the T type parameter becomes int[].

Quevedo answered 10/12, 2012 at 19:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.