Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Generics and unchecked cast

Tags:

java

generics

I'm struggling with this aspect of Generics in Java. Hopefully someone can help me see the ways.

I have a class that holds a List of objects. This code works, but I want to get rid of the cast. How can I make this more generic?

public class Executor {
    List<BaseRequest<BaseObj>> mRequests = new ArrayList<BaseRequest<BaseObj>>();

    public Executor() {
    }

    @SuppressWarnings("unchecked")
    public <T extends BaseObj> void add(final BaseRequest<T> request) {
        mRequests.add((BaseRequest<BaseObj>) request);
    }

    public void execute() {
        for (BaseRequest<BaseObj> r : mRequests) {
            // DO SOMETHING WITH r
        }
    }
}
like image 867
Drew Avatar asked Dec 18 '13 15:12

Drew


People also ask

What is unchecked cast in Java?

2. What Does the “unchecked cast” Warning Mean? The “unchecked cast” is a compile-time warning. Simply put, we'll see this warning when casting a raw type to a parameterized type without type checking. An example can explain it straightforwardly.

How do I fix an unchecked cast warning?

You're using a raw type in your code, which is giving you the unchecked cast problem. You normally fix this by using a fully parameterized class instead: Comparable<Whatever>.

What is unchecked assignment in Java?

Unchecked assignment: 'java.util.List' to 'java.util.List<java.lang.String>' It means that you try to assign not type safe object to a type safe variable. If you are make sure that such assignment is type safe, you can disable the warning using @SuppressWarnings annotation, as in the following examples.


1 Answers

In the posted snippet you need the cast because BaseRequest<? extends BaseObj> is not a subtype of BaseRequest<BaseObj>, and the cast can't be checked at runtime because of type erasure, and that's why the compiler warns you. But if you change the declaration of mRequests:

public class Executor  {

    List<BaseRequest<? extends BaseObj>> mRequests = new ArrayList<>();

    public Executor() {
    }

    public <T extends BaseObj> void add(final BaseRequest<T> request) {
        mRequests.add(request);
    }

    public void execute() {
        for (BaseRequest<? extends BaseObj> r : mRequests) {
            // DO SOMETHING WITH r
        }
    }
}

class BaseRequest<T> {}

class BaseObj {}

Let's resolve the problem step-by-step. You want to be able to call

req.add(new BaseRequest<ExtObj1>());
req.add(new BaseRequest<ExtObj2>());
req.add(new BaseRequest<ExtObj3>());

where ExtObj[1|2|3] extends BaseObj. Given the List interface:

List<T> {
  void add(T el);
}

we need to find a common supertype for BaseRequest<ExtObj1>, BaseRequest<ExtObj2> and BaseRequest<ExtObj3>. One supertype is BaseRequest<?> and another one is BaseRequest<? extends BaseObj>. I picked the second one because it's the most restrictive possible. You should know that in Java BaseRequest<ExtObj1> is not a subtype of BaseRequest<BaseObj> because generics are invariant.

Now that we have the right declaration for mRequests, finding the API for Executor.add() is straightforward. BTW, if the method body you need is really that simple, you don't even need the type parameter:

public void add(BaseRequest<? extends BaseObj> request) {
  mRequests.add(request);
}
like image 65
Raffaele Avatar answered Oct 10 '22 06:10

Raffaele