I ran into this problem recently when I was playing around. Basically, I wanted to make a central class where consumers of a certain type could register themselves. Something then publishes objects for the consumers, and only those consuming the published type should receive it.
The entire program can be summarised to this:
public class Main
{
public interface Fruit
{
}
public class Apple implements Fruit
{
}
public class Banana implements Fruit
{
}
public interface FruitConsumer<T extends Fruit>
{
public void consume(T fruit);
}
public class Consumers<T extends Fruit>
{
Map<Class<T>, Collection<FruitConsumer<T>>> consumers = new HashMap<>();
public void register(Class<T> clazz, FruitConsumer<T> consumer)
{
consumers.putIfAbsent(clazz, new HashSet<>());
consumers.get(clazz).add(consumer);
}
public void consume(T fruit)
{
Collection<FruitConsumer<T>> cons = consumers.get(fruit.getClass());
for (FruitConsumer<T> c : cons)
{
c.consume(fruit); // <-- Breaks if T is Apple
}
}
}
public class BananaConsumer implements FruitConsumer<Banana>
{
void register(Consumers c)
{
c.register(Banana.class, this);
c.register(Apple.class, this); // <-- Why does this work?
}
@Override
public void consume(Banana banana)
{
System.out.println("Mmm, banana");
}
}
public static void main(String... args)
{
Main main = new Main();
main.run();
}
private void run()
{
Consumers consumers = new Consumers<>();
BananaConsumer bananaConsumer = new BananaConsumer();
bananaConsumer.register(consumers);
Banana banana = new Banana();
consumers.consume(banana);
Apple apple = new Apple();
consumers.consume(apple);
}
}
Now, I think I understand why this crashes. There is no way for the compiler to know that the T
in the Consumers.register
method is the same T
, for both parameters. The compiler can only enforce that both arguments meets the requirement T extends Fruit
. I want to remember being able to use a similar code structure in C++, so there has to be something the two languages does different. Is my assumption here correct?
Also, what would be the correct way of going about this? I would like different consumers of subtypes of Fruit to be able to register themselves, and receive only those fruits they enlist themselves as consumers of. Also, I would like some safety in what you can register yourself as a consumer off (I noticed that this code works as long as no one registers themselves "wrong").
Lastly, what is the name of this phenomena? Basically, what do I google to learn more about this.