views:

911

answers:

4

I have a List that is guaranteed to contain just one type object. This is created by some underlying code in a library that I cannot update. I want to create a List<ObjectType> based on the incoming List object so that my calling code is talking to List<ObjectType>.

What's the best way to convert the List (or any other object collection) to a List<ObjectType>.

+5  A: 

You can simply cast the list:

  List raw = new ArrayList();
  List<String> generic = (List<String>) raw;
daveb
If the list is indeed guaranteed to hold one type of item, this is a good answer.
Michael Myers
That is a big "if". It depends very much on the context.
erickson
I can guarantee that the list will hold the one type of item. This was code written in Java 1.4 pre-generics.
Shaun F
This is not type-safe in general because it requires that the list be homogenous; meaning that all of the objects in the list are of the same type. If the list contains an object which is of a different type than the others, the program may later throw a `ClassCastException` and this exception won't point you to where the list was created.
Derek Mahar
In order to avoid the situation that I described in my previous comment, the Eclipse Java compiler, when it encounters a cast from a legacy list to a generic list, generates the warning, "Type safety: The expression of type List needs unchecked conversion to conform to List<String>". Programmers who ignore these warnings at compilation time do so at their own peril at runtime.
Derek Mahar
+5  A: 

When inter-operating with legacy code that doesn't specify type parameters for generic types, use a wildcard. For example, suppose you are calling a method in an older library that simply returns a raw Collection:

Collection getItems();

In your code, assign the result to a variable declared with a wildcard:

Collection<?> items = widget.getItems();

This way, you preserve type safety so you won't get any warnings.

The legacy code might specify (in a comment, most likely) what the generic parameters should be. For example:

/**
 * @return the items, as a Collection of {@link Item} instances.
 */
Collection getItems();

In this case, you have a choice. You can cast the result to a Collection<Item>, but if you do so, you are relying 100% on the third-party library, and discarding the assurance of Java generic types: that any ClassCastException raised at runtime will occur right at an explicit cast.

What if you don't fully trust the third-party library, but still need to produce a Collection<Item>? Then create a new collection, and add the contents after casting them to the expected type. That way, if there is a bug in the library, you find out about it right away, rather than having some code far away and much later mysteriously blow up with a ClassCastException.

For example:

Collection<?> tmp = widget.getItems();
Collection<Item> items = new ArrayList<Item>(tmp.size());
for (Object o : tmp)
  items.add((Item) o); /* Any type error will be discovered here! */

For a case where the type parameter isn't known at compile-time, you can use the type-checked collection factories of the Collections class.

erickson
The problem is, that doesn't really gain you anything other than silencing the "raw type" warnings. Converting to generics would imply making use of its benefits!
Andrzej Doyle
What is the benefit of generics? It's not to make your code look pretty and require less typing! It is this: *Your code is guaranteed to be type-safe if it compiles without warnings.* That is it. When you ignore or suppress the warnings, you might as well not use generics at all, because all you are doing is creating situations where code without a cast operator mysteriously raises `ClassCastException`.
erickson
+1  A: 

The best and safest way is to use java.util.Collections method 'checkedList(List list, Class type)'

With this method, all of the items in your old List will be checked as early as possible.

nanda
Doesn't `checkedList` in Java 5 apply only to new (and empty) generic lists? Are you suggesting that we use the non-generic `checkedList` prior to Java 5? This legacy form won't convert to a generic list.
Derek Mahar
A: 

If you just cast to List<T> in any old place you will get an "unchecked" compiler warning. We resolved that by moving it to a utility method.

public class Lists {
    @SuppressWarnings({"unchecked"})
    public static <T> List<T> cast(List<?> list) {
        return (List<T>) list;
    }
}

Caller now gets no warning, e.g.:

for (Element child : Lists.<Element>cast(parent.getChildren())) {
    // ...
}

That checkedList utility is in theory a great idea, but in practice it sucks to have to pass in the class you expect. I hope Java will get runtime generic typing information eventually.

Trejkaz