The tricky case of type generators in Java
I would like to have a type in Java that references itself in a specific way.
To be precise, I want to have a command class that can have listeners:
public abstract class GenericCommand<T> implements Future<T> {
// ...
private final List<GenericCommandListener<GenericCommand<T>>> listeners = new ArrayList<>();
@SuppressWarnings("unchecked")
public void addListeners(
GenericCommandListener<GenericCommand<T>>... listeners) {
this.listeners.addAll(Arrays.asList(listeners));
}
private void onValueAvailable() {
for (GenericCommandListener<GenericCommand<T>> listener : listeners) {
listener.onValueAvailable(this);
}
}
}
A GenericCommandListener
looks like
public interface GenericCommandListener<T extends GenericCommand<?>> {
public void onValueAvailable(T theCmd);
}
The purpose of such a command is to send to the device and produce a result of a certain type.
Now I want to override mine GenericCommand<T>
so that it implements a special type of command, perhaps FooCommand
that creates Double
:
public class QueryValueCommand extends GenericCommand<Double> {
}
Now I am instantiating this class and want it to accept GenericCommandListener<QueryValueCommand>
. But I cannot do this; all I can do is accept GenericCommandListener<GenericCommand<Double>>
.
I see the following ways:
-
Do something with
? super
or? extends
in the definition of class listenersGenericCommand
. I tried several combinations but didn't work as either the object can't be put into the list or the call doesn't work. -
Change the definition of the Listener class, but how?
-
Define the class in a
GenericCommand
different way so that it always uses exactly the correct type of listener references:public abstract class GenericCommand<T> implements Future<T> { private final List<GenericCommandListener<MyExactTypeEvenIfSubclassing>> listeners = ...; }
so what is derived from it
QueryValueCommand
takes on a valueGenericCommandListener<QueryValueCommand>
?
source to share
As suggested by aruisdante, the class GenericCommand
should be like this:
public abstract class GenericCommand<C extends GenericCommand<C, T>, T> implements Future<T> {
// ...
private final List<GenericCommandListener<C, T>> listeners = new ArrayList<>();
@SuppressWarnings("unchecked")
public void addListeners(
GenericCommandListener<C, T>... listeners) {
this.listeners.addAll(Arrays.asList(listeners));
}
private void onValueAvailable() {
for (GenericCommandListener<C, T> listener : listeners) {
listener.onValueAvailable((C) this);
}
}
}
The listener class should also change like this:
interface GenericCommandListener<C extends GenericCommand<C,T>, T> {
public void onValueAvailable(C theCmd);
}
Now you can declare:
public class QueryValueCommand extends GenericCommand<QueryValueCommand, Double>
and you can write:
QueryValueCommand command = new QueryValueCommand();
GenericCommandListener<QueryValueCommand, Double> listener = ...;
command.addListeners(listener);
source to share
If you implement GenericCommandListener
like this:
public interface GenericCommandListener<T> {
public <U extends GenericCommand<T>> void onValueAvailable(U theCmd);
}
Then you can add listeners to your class GenericCommand
like this:
public abstract class GenericCommand<T> implements Future<T> {
private final List<GenericCommandListener<T>> list = new ArrayList<>();
public <U extends GenericCommandListener<T>> void addListener(U listener) {
list.add(listener);
}
Does it help?
source to share