javagenericsswing-app-frameworkjsr296

Understanding the Generic type parameters of TaskListener


I'm currently working on refactoring some old code and I found a snippet where I don't understand how to properly use Generics for the Swing Application Framework class TaskListener.Adapter.

This is the relevant code snippet:

public void executeTask(Task<?, ?> task, boolean handleException) {
    task.addTaskListener(new TaskListener.Adapter() { /* <-- Two warnings here */
        @Override
        public void failed(TaskEvent event) { /* ... */ }
    });
    getContext().getTaskService().execute(task);
}

1. First I want to get rid of the warnings. "unchecked conversion" and "found raw type". I tried to change the code to new TaskListener.Adapter<Object, Object>, but then I get the error "cannot be applied to given types". Is a raw type the only thing I can use here because of the (Task<?, ?> declaration?

2. The declaration of the failed method in org.jdesktop.application.TaskListener.Adapter is public void failed(TaskEvent<Throwable> event), but if I try to change my code to this:

@Override
public void failed(TaskEvent<Throwable> event) { /* ... */ }

I get "method does not override a method from a supertype". Again I have to go with the raw TaskEvent. Why is that?

Thank you for your help.

EDIT: Javadoc for TaskListener on Jarvana.


Solution

  • You can't apply an adapter genericised as <Object, Object> to a Task<?, ?> because who knows what the wildcards are? It could be a Task<String, Integer> in which case the adapter's bounds wouldn't match and so it wouldn't be applicable.

    (Well, it might be that in this case they are OK because they're consumers, but the compiler can't infer that by itself. If this is the case, then Task.addTaskListener needs to be declared to take a TaskListener<? super T, ? super V> in order to satisfy the compiler.)

    Because of the way the addTaskListener is declared, you need to pass in a listener with the exact same generic bounds. And this is impossible when you declare them as wildcards, since you have no way to refer back to them later. What you need to do instead is make the method generic, which is similar to using ? wildcards except you're giving them names so you can refer to them later on in the method:

    public <T, V> void executeTask(Task<T, V> task, boolean handleException) {
        task.addTaskListener(new TaskListener.Adapter<T, V>() {
            @Override
            public void failed(TaskEvent event) { /* ... */ }
        });
        getContext().getTaskService().execute(task);
    }
    

    As for the second part, I have no idea - it looks fine to me as well. Perhaps this is some misleading error that comes from the raw types, although that seems unlikely since neither generic parameter is involved in the declaration at all. If the compiler still doesn't like the override after the above changes, then perhaps the problem is somewhere else (like you've accidentally placed the method in the wrong class' scope, etc.)