Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Complicated case of type generics in Java

Tags:

java

generics

I would like to have a type in Java which references itself, in a certain way.

To be exact, I want to have a command class which 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 be sent to a device and to produce a result of a certain type.

Now, I want to be able to override my GenericCommand<T> so that it implements a special kind of command, maybe a FooCommand, which produces a Double:

public class QueryValueCommand extends GenericCommand<Double> {
}

Now, I create an instance of this class and want it to accept a GenericCommandListener<QueryValueCommand>. But I cannot do so; all I can do is to have it accept a GenericCommandListener<GenericCommand<Double>>.

I see the following ways:

  1. Do something with ? super or ? extends in the listeners definition of the GenericCommand class. I tried several combinations, but neither did work, as either the object cannot be put into the list, or the call does not work.

  2. Change the definition of the Listener class - but how?

  3. Define the GenericCommand class in a different way so that it always uses references to listeners of the exactly correct type:

    public abstract class GenericCommand<T> implements Future<T> {
        private final List<GenericCommandListener<MyExactTypeEvenIfSubclassing>> listeners = ...;
    }
    

    so that the QueryValueCommand derived from it accepts a GenericCommandListener<QueryValueCommand>?

like image 201
glglgl Avatar asked Aug 15 '26 06:08

glglgl


1 Answers

As proposed by aruisdante, the class GenericCommand must depends from itself that way :

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 must also by modified that way :

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);
like image 114
Serge Ballesta Avatar answered Aug 16 '26 20:08

Serge Ballesta