Friday, 3 February 2017

Java JPA and Generics and Type Erasure: Casting a List to List

Java Generics are a bundle of compromises and workarounds. Backwards compatibility is a PITA. We all know that. Anyway... 

I had a service class that, due to some refactoring, was now going to be returning a List<ParentClass> whereas before it could return a List<ChildClass>. We had some A/B testing inside the implementation, so in some cases, we would be actually returning a List<ChildClassTwo> from our DAO class.

My original fix for this was to create a new ArrayList and initialise it with the List of ChildClass or ChildClassTwo, but I didn't like the idea of having to create a new object just to change types.

But there's actually a better way, one that does not involve creating an extra wrapper object, that I found on StackOverflow.

To use the classic O-O hierarchy of  Animal and Dog classes:

List<Dog> dogsInLocation = dao.findDogs(location);

List <Animal> animalsInLocation  = (List<Animal>) (List) dogsInLocation;

So basically you are casting the List<Dog>  "up" to a List, and then casting it "down" to a List<Animal>

Trying to do 

 (List<Animal>)  dogsInLocation;

gives you an "Inconvertible types" error.  You can't directly go from a typed list of one type to a type of another. 

The workaround is to use type erasure, turn it into a standard List, which gets rid of the type information about List<Dogs>, and *then* cast it to a List<Animal>

No comments: