First I will try to explain the idea behind this code. I have a bunch of classes (Processors) that can process a certain type of other classes (Processables). I have a List of Processors to execute them in a certain order. I have a Map that will retrieve me the data to process (Processables) for a certain Processor. It looks somehow like this.
public abstract class AbstractProcessable {
...
}
public class DummyProcessable extends AbstractProcessable {
...
}
public abstract class AbstractProcessor<T extends AbstractProcessable> {
public abstract void process(List<T> listOfProcessables);
}
public class DummyProcessor extends AbstractProcessor<DummyProcessable> {
@Override
public void process(List<DummyProcessable> listToProcess) {
...
}
}
This seems to work fine so far. There are no compilation errors. But now I have a class like the following:
public class RandomClass {
private List<AbstractProcessor<? extends AbstractProcessable>> processors;
private Map<Class<? extends AbstractProcessor>, List<? extends AbstractProcessable>> data;
public RandomClass() {
processors = new ArrayList<>();
processors.add(new DummyProcessor());
data = new HashMap<>();
data.put(DummyProcessor.class, new ArrayList<DummyProcessable>());
processAll();
}
private void processAll() {
for (AbstractProcessor<? extends AbstractProcessable> processor : processors) {
List<? extends AbstractProcessable> dataToProcess;
dataToProcess = data.get(processor);
processor.process(dataToProcess); // compile error
}
}
}
Compile error:
The method process(List<capture#4-of ? extends AbstractProcessable>) in the type AbstractProcessor<capture#4-of ? extends AbstractProcessable> is not applicable for the arguments (List<capture#5-of ? extends AbstractProcessable>)
I know it might be a bit difficult to read, but I tried to simplify it as much as possible. I'm also not that good with generics so maybe I used some wildcards wrong? Can anyone help me to solve that problem?
Thanks a lot in advance!
If I understand your question correctly, I asked a similar one some time ago. Short answer is that you can't use such a map in the way you intend to. The good news is that you don't need it ;)
The problem is that Java generics are a compile-time thing (as you may already know), and they solely exist at compile time, which is not when you actually fill your List
. You cannot find a way to express your idea in Java, even if it seems a perfectly legitimate one.
You may find Super Type Tokens useful to extract parameters in certain cases and avoid the user to supply an explicit parameter if it's suitable (however mind the limitations and consider if they can really add some value).
In short, you'll end up with a field like
private Map<Class<?>, List<?>> data;
(or you can use Guava's Multimap
implementation for this), using some casts and shouting some warnings out, and relying on your program logic for the client code to be type safe. I used that approach in my code and it never failed so far.
This is my code. I think it's exactly your case, just replace Subscriber<T>
and Message
with your Processor<T>
and Processable
public class MessageBus {
public enum Action {
CREATE, REMOVE, UPDATE, DELETE;
}
private static Map<Class<?>, Set<Subscriber<?>>> subscriptions;
static {
subscriptions = new HashMap<Class<?>, Set<Subscriber<?>>>();
}
@SuppressWarnings("unchecked")
public static <T> void publish(T message, Action action) {
Set<Subscriber<?>> set = getSubscribersFor(message.getClass());
if (set == null)
return;
for (Subscriber<?> subscriber: set) {
((Subscriber<T>) subscriber).onMessage(message, action);
}
}
public static <T> void subscribe(Class<T> type, Subscriber<T> subscriber) {
Set<Subscriber<?>> set = getSubscribersFor(type);
if (set == null) {
set = new HashSet<Subscriber<?>>();
subscriptions.put(type, set);
}
set.add(subscriber);
}
public static <T> void unsuscribe(Class<T> type, Subscriber<T> subscriber) {
Set<Subscriber<?>> set = getSubscribersFor(type);
set.remove(subscriber);
}
private static Set<Subscriber<?>> getSubscribersFor(Class<?> topic) {
return subscriptions.get(topic);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With