Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I convert from List<?> to List<T> in Java using generics?

In Java, how do I convert List<?> to List<T> using a general purpose method so that I can replace patterns like the following with a single method call:

List untypedList = new ArrayList();  // or returned from a legacy method
List<Integer> typedList = new ArrayList<Integer>();
for (Object item: untypedList)
    typedList.add((Integer)item);

Note that the above code does not generate any type-safety warnings and, ideally, your solution shouldn't generate any such warnings, either.

Will the following solution work provided that list Class<L> has a public default constructor?

public class ListUtil {
    public static <T, L extends List<T>> L typedList(List<?> untypedList, Class<T> itemClass, Class<L> listClass) {
        L list = null;
        try {
            list = listClass.newInstance();
        } catch (InstantiationException e) {
        } catch (IllegalAccessException e) {
        }
        for (Object item: untypedList)
            list.add(itemClass.cast(item));
        return list;
    }
}

(Note that listClass.newInstance() throws InstantiationException or IllegalAccessException if an instance of Class<L> does not have a public default constructor. What problems may arise if the method does not properly handle these exceptions?)

Notes:

  • T is the type of each item in the resulting list.
  • L is the type of the list that I wish to create (which extends List<T>).
  • untypedList is the "untyped" input list, effectively the same as List<Object>.
  • itemClass represents the runtime class of T.
  • listClass represents the runtime class of L.
like image 919
Derek Mahar Avatar asked Aug 23 '10 14:08

Derek Mahar


People also ask

How do you use T generics in Java?

A Generic Version of the Box Class To update the Box class to use generics, you create a generic type declaration by changing the code "public class Box" to "public class Box<T>". This introduces the type variable, T, that can be used anywhere inside the class.

What does list <> mean in Java?

In Java, a list interface is an ordered collection of objects in which duplicate values can be stored. Since a List preserves the insertion order, it allows positional access and insertion of elements. List interface is implemented by the following classes: ArrayList. LinkedList.

What does T stand for in Java generics?

< T > is a conventional letter that stands for "Type", and it refers to the concept of Generics in Java. You can use any letter, but you'll see that 'T' is widely preferred. WHAT DOES GENERIC MEAN? Generic is a way to parameterize a class, method, or interface.


2 Answers

I would use Guava and its Iterables.filter(Iterable,Class) method along with a factory method from the Lists class, like so:

List<?> original = ...;
List<String> typed = Lists.newArrayList(
   Iterables.filter(original, String.class));

This will actually check each object in the original list and the resulting list will contain only those elements that are instances of the given type (String in this case) or a subtype of it. I really don't think it makes sense to have users provide a Class for the resulting List type and try to instantiate it via reflection.

like image 74
ColinD Avatar answered Sep 28 '22 03:09

ColinD


Rather than passing in the type of the list you want to instantiate, why not just pass in the empty Collection<T> that you want populated? This gives the users of your api much more flexibility, as using the default constructor is not always ideal. (for example, maybe I want a Set where I provide the expected number of elements, or I want a sorted list where I provide the Comparator).

Also, as a side note, you should always program to the most generic interface possible. In this case, your input need be nothing more specific than an Iterable, and your output a Collection.

Given this, I would write the method this way --

  public static <T, C extends Collection<T>> C typesafeAdd(Iterable<?> from, C to, Class<T> listClass) {
    for (Object item: from) {
      to.add(listClass.cast(item));
    }
    return to;
  }

then the calling code looks like:

  public static void main(String[] args) {
    List<?> untypedStringList = LegacyApi.getStringList();
    List<String> typesafeStringList = typesafeAdd(untypedStringList, new ArrayList<String>(), String.class);
  }

2 Comments here:

  • If you can really trust LegacyApi (or whatever provided you the untyped List) to only return to you a collection with the expected type in it, then you can just do an unchecked cast and suppress it. This should be localized to the smallest scope possible. ie: create something like TypesafeLegacyApiWrapper which delegates calls to LegacyApi.
  • This method signature still breaks down if you have anything more complicated. For example if you have a List<List<String>> this method does not work.
like image 39
Michael D Avatar answered Sep 28 '22 04:09

Michael D