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
.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.
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.
< 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.
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.
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 love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With