views:

1794

answers:

4

In the spirit of Best Practices: Always return a ____, never a ____, I face a similar question in my upcoming migration from JDK1.4.2 to JDK5 and more. (Yes, I know, JDK1.4.2 is EOL! ;-) ).

For functions returning a collection (which are not simple property collections), I always prefer (in JDK1.4.2) returning an Array instead of a generic List, because:

  • it enforces the returning type (MyObject[] instead of List of Objects, much more type-safe on a static -- as in 'compilation' -- level)
  • it suggests a 'read-only' character to the returned collection (it is more complicated to add an element to the collection, even though this is not as rigorous as the 'read-only' keyword in c#). This is not the same as saying it is 'immutable' since the references inside the array can still be modified...

Of course, I do always create this returned array (I do not expose any 'internal' array)

Now, In JDK5 and more, I could use List<MyObject> if I want to.

What are the good reasons for choosing to return MyObject[] instead of List or Collection<MyObject> when coding in java5 ?

Bonus, if Collection<MyObject> is used, is it possible to:

  • enforce a read-only attribute on the returned collection ? (no add() or remove() possible)
  • enforce an immutable aspect to the returned collection ? (even the references of that collection can not be modified)

PS: The JavaGenericFAQ did not quite have that one.

+27  A: 

Prefer Collection (or List, or Set as appropriate) to an array. With generics you get the type-checking that was lacking pre-Java 5. Also, by exposing only the interface, you are free to change the implementation later (e.g. switch an ArrayList for a LinkedList).

Arrays and generics don't mix very well. So, if you want to take advantage of generics, you should usually avoid arrays.
I.e: You can't generically create an array. For example, if T is a generic type then "new T[0]" doesn't compile. You'd have to do something like "(T[]) new Object[0]", which generates an unchecked cast warning. For the same reason, you can't use generic types with varargs without warnings.

Using Collections.unmodifiableCollection (and similar methods), you get the read-only constraint (which you can't achieve with an array - you would have to return a clone of the array).

You can't enforce immutability of members, but then you can't do that with an array either.

Dan Dyer
Thank you! +1 for now (I will wait a little for other answers). You can tell I have not yet seriously read the JDK6 javadoc... ;)
VonC
"Arrays and generics don't mix very well. So, if you want to take advantage of generics, you should usually avoid arrays." -> Urg? can you explain this part? My arrays mix well with my generics...
Nicolas
You can't generically create an array. For example, if T is a generic type then "new T[0]" doesn't compile. You'd have to do something like "(T[]) new Object[0]", which generates an unchecked cast warning. For the same reason, you can't use generic types with varargs without warnings.
Dan Dyer
Thank you Dan, thank you all, this has been very helpful.
VonC
+3  A: 

Check out the unmodifiable* methods in Collections class to make returned collections read-only/immutable. This will wrap the original collection in a list that prevents access to changing the list itself (the elements themselves are not immutable though).

unmodifiableList for instance.

For me it depends on how the returned list will be used. What other methods is it being passed to, and how will it be used? In general I'd prefer the generic collection over an array, but would change it on a case by case basis.

The array will probably perform faster as well if you're doing something critical.

madlep
no real-time process involved here. And the collection does not represent a "property", just computed value.
VonC
+1  A: 

My answer was downvoted because i use proxy for class and using those kind of things (proxy and reflection) is forbitten because it impact performance of some people's minds(many people don't even use reflection, however they use hibernate and spring which use relfection, class proxy and xml).

Enhancer from cglib.sourceforge.net project, it allows to create proxy for classes(JDK supports only proxies for Interfaces). Proxy allows you to control methods in object, however you cant access fields using reflections. if you have some questions ask.

Thank you for the code and precisions. +1, but I might not use that kind of extra layer in my code anytime soon...
VonC
I understand, but what I really want to say that its like using Collections.unmodifiableCollection. It gives you nothing until your collection is modify not by you.
+1  A: 

Actually arrays still have one advantage over Collections/Lists. Due to the way that Java implements Generics through type erasure, you cannot have two methods that take Collections as arguments yet only differ by the Collection's generic type.

Ex:

public void doSomething(Collection<String> strs) { ... }
public void doSomething(Collection<Integer> ints) { ... }

The two above methods will not compile because the javac compiler uses type erasure and thus cannot pass the type information to the JVM. The JVM will only see two methods that take a Collection as its argument.

In the above cases, the best work-around is to make the methods take arrays as their arguments and use the Collection/List's toArray() method when passing the arguments to them. If you still want to use Collection/List's inside the above methods, just use java.util.Arrays.asList() method to get your List back.

Ex:

public void doSomething(String[] strs) {
        List<String> strList = Arrays.asList(strs);
        ...
}

public void doSomething(Integer[] ints) {
        List<Integer> intList = Arrays.asList(ints);
        ...
}

public static void main(String[] args) {
        List<String> strs = new ArrayList<String>();
        List<Integer> ints = new ArrayList<Integer>();
        obj.doSomething(strs.toArray());
        obj.doSomething(ints.toArray());
}
mcjabberz
Excellent point. Thank you for pointing that out.
VonC