【发布时间】:2018-07-23 15:13:44
【问题描述】:
我最近在玩的时候遇到了这个问题。基本上,我想创建一个中心类,某种类型的消费者可以在其中注册自己。然后某些东西会为消费者发布对象,只有那些使用发布的类型的人才能收到它。
整个程序可以概括为:
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);
}
}
现在,我想我明白为什么会崩溃了。对于两个参数,编译器无法知道Consumers.register方法中的T 与T 相同。编译器只能强制两个参数都满足T extends Fruit 的要求。我想记住能够在 C++ 中使用类似的代码结构,因此这两种语言必须有所不同。我的假设是否正确?
另外,正确的方法是什么?我希望水果子类型的不同消费者能够注册自己,并且只接收他们自己作为消费者的水果。另外,我希望您可以将自己注册为消费者,以确保安全(我注意到只要没有人将自己注册为“错误”,此代码就可以工作)。
最后,这种现象的名称是什么?基本上,我要通过 Google 搜索来了解更多信息。
【问题讨论】:
-
您可以查找“类型安全的异构容器”。基本上你不能仅仅通过使用泛型来保证这个安全,你必须依赖封装。
-
请参阅stackoverflow.com/q/44422685/2891664 以了解您正在尝试做的一些示例。 (另外,我可能错了,但我认为这种事情在 C++ 中也不方便,除非你试图使用类到消费者的映射模式作为 C++ 模板的笨拙替代品专业。)
-
感谢您的指点。我将阅读这些主题,看看如何重建它以确保安全。我从来没有听说过“类型安全的异构容器”这个词,非常感谢!