【问题标题】:Mapping class A (and all subclasses) to instance of class B in Java将 A 类(和所有子类)映射到 Java 中 B 类的实例
【发布时间】:2016-01-07 11:25:57
【问题描述】:

在 Java 中实现策略模式时,存在将输入数据类型映射到特定算法实例的常见问题。

例如,我们可以创建一个地图:

map.put(Apple.class, new AppleHandler());
map.put(Orange.class, new OrangeHandler());

并在运行时为给定的数据类型解析正确的处理程序:

Apple a = new Apple();
map.get(a.getClass()).handle(a);

问题:Guava/Apache Commons 等库中是否有任何Map<Class<A>, B> 实现可以处理多态类型?

例如上面的映射会像这样工作(假设 GreenApple 扩展了 Apple):

  map.get(Apple.class) == map.get(GreenApple.class);

我知道我可以编写遍历层次结构的实现,但我问的是 OTS 解决方案。

【问题讨论】:

  • Guava 有ClassToInstanceMap,但这固定了值类型(到键的实例)。我有一个实现 here 但请注意,当解析类型时存在歧义时,这个实现会发出嘶哑的声音。由于接口本质上具有多重继承,因此获取最具体的类型并不总是可能的。
  • 感谢您的要点 - 我正在使用类似的实现(我认为在模棱两可的情况下运行时异常是可以接受的)。我只是认为这是一个很常见的问题,并且有一些“官方”解决方案。
  • “OTS 解决方案”是什么意思?
  • @mfulton26 Off-The-Shelf - 意思是“现有的、随时可用的解决方案”,就像一个公开可用的库。
  • 我认为目前的共识是不,没有现成的。 (当然不是番石榴。)

标签: java dictionary polymorphism guava strategy-pattern


【解决方案1】:

最简单的解决方案是为具有多个类的处理程序添加多个映射条目:

map.put(Apple.class, new AppleHandler());
map.put(GreenApple.class, map.get(Apple.class));
map.put(Orange.class, new OrangeHandler());

这种方法的一些注意事项包括

  1. 如果您有很多类,那么您的地图可能会有点大。
  2. 如果外部源可以定义 A 类型的新类并在运行时提供它们,那么您还需要某种方式来注册它们(例如,通过在初始化时扫描类路径,为外部代码提供某种注册 API 以如果请求的键不在地图中,则使用或添加支持遍历层次结构)。

但是,如果您可以控制源代码,那么您可以在编译时在类本身中进行这种“映射”:

interface A {
    B getHandler();
}

interface B {
}

class Apple implements A {
    public B getHandler() {
        return AppleHandler.INSTANCE;
    }
}

class GreenApple extends Apple {
}

class Orange implements A {
    public B getHandler() {
        return OrangeHandler.INSTANCE;
    }
}

现在您可以通过简单地调用getHandler() 方法来获取A 类的任何实例的处理程序。由于getHandler() 是合同的一部分,定义A 新类的外部源也必须实现它。

如果您不能更改A 类型的来源或不想更改(更改为separate concerns 等),那么您可以引入一个新类型包装A 以使用@987654322 定义映射@。

【讨论】:

  • 感谢您的输入,但我想解决多态映射的问题;我知道类映射的问题可以通过许多不同的方式来解决。抱歉,标题太笼统了。
【解决方案2】:

我已经通过注释解决了类似的问题

@Documented @Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Handler {
    Class<? extends AbstractHandler> value();
}

这样我得到一个处理程序类而不是处理程序本身,但在我的情况下这很好。您可以使用Class#newInstance 或依赖注入来获取处理程序实例。


另一种可能性是像这样的查找循环

for (Class<?>  cl = givenClass; cl != null; cl = cl.getSuperclass()) {
    Class<? extends AbstractHandler> result = map.get(cl);
    if (result != null) {
        return result;
    }
}
... handle "handler not found"

这适用于超类,但不适用于接口。它可以为他们扩展,但你可能会遇到the diamond problem

我做过一次,钻石问题对我来说没问题,因为我的处理程序被严格优先考虑。我最终优化的解决方案是使用ConcurrentMap&lt;Class&lt;?&gt;, &lt;Class&lt;AbstractHandler&gt;&gt;&gt;,每次遇到新课程时都会更新。

【讨论】:

  • 谢谢,我知道它可能看起来像这样,我想知道某些库中是否有现成的实现。抱歉,如果问题不清楚。
  • @ZbigniewMalinowski 不幸的是,有关图书馆的问题是这里的主题。但是,如果我知道任何事情,我会在答案中提到它。我想,这个问题还不够普遍(并且它的实例有不同的要求,例如,由于菱形问题)。
猜你喜欢
  • 1970-01-01
  • 2022-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多