Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

java generics - cast to List<SomeType> issues unchecked cast warning while cast to SomeType not

Why does this:

public <T> List<byte[]> getData(T data) {
    Location loc = (Location) data;
    // ...
}

not generate any warnings, while this:

public <T> List<byte[]> getData(T data) {
    List<ScanResult> scanRes = (List<ScanResult>) data;
    // ...
}

generates Type safety: Unchecked cast from T to List<ScanResult>?

How can I resolve the warning?
As a design is this kind of method declaration a smell?

public <T> List<byte[]> getData(T data)

is an interface method implemented in different classes with different data types - the first line of all implementations is such a cast

like image 307
Mr_and_Mrs_D Avatar asked Sep 09 '13 19:09

Mr_and_Mrs_D


2 Answers

You get the warning because the cast (List<ScanResult>) data is not safe. Due to type erasure, List<ScanResult> will be List during runtime, so there will be no real type check regarding the element type of the list. That is, that cast will succeed even if you get List<String> as a parameter and later you will get a ClassCastException when you try to access the list:

ScanResult result = data.get(0); // ClassCastException: String

One way to avoid it is making the interface generic:

public interface DataProvider<T> {
    public List<byte[]> getData(T data);
}

And then define the specific type argument at implementations:

public class DataProviderFromLocation implements DataProvider<Location> {
    public List<byte[]> getData(Location data) {
    }
}

public class DataProviderFromScanResultList implements DataProvider<List<ScanResult>> {
    public List<byte[]> getData(List<ScanResult> data) {
    }
}

I don't know if it is suitable for your needs.

like image 135
Katona Avatar answered Oct 17 '22 14:10

Katona


From Angelika Langer's Java Generics FAQs

We are prepared to cope with ClassCastException s when there is a cast expression in the source code, but we do not expect ClassCastException s when we extract an element from a list of strings. This sort of unexpected ClassCastException is considered a violation of the type-safety principle. In order to draw attention to the potentially unsafe cast the compiler issues an "unchecked" warning when it translates the dubious cast expression.

So the answer to my first question is that the cast to SomeType will fail there and then if the classes are not compatible - while the List<ScanResult> scanRes = (List<ScanResult>) data; which at run time is just List scanRes = (List) data; won't fail if data is any List implementation - but might result in a CCE in a remote and completely unrelated part of the codebase - hence it will be real difficult to debug - hence the warning.

Another way to put it (by @erickson here) :

By doing your own cast up front, you're "complying with the warranty terms" of Java generics: if a ClassCastException is raised, it will be associated with a cast in the source code, not an invisible cast inserted by the compiler.

like image 28
Mr_and_Mrs_D Avatar answered Oct 17 '22 13:10

Mr_and_Mrs_D