我遇到了一个棘手的问题,我似乎无法用 java 泛型解决。这有点复杂,但我想不出一个更简单的场景来说明这个问题......这里是:
我有一个需要上下文的处理器类。有不同类型的上下文;大多数处理器只需要任何抽象上下文,但其他处理器需要特定的子类。像这样:
abstract class AbstractProcessor<C extends Context> {
public abstract void process(C context);
}
class BasicProcessor extends AbstractProcessor<Context> {
@Override
public void process(Context context) {
// ... //
}
}
class SpecificProcessor extends AbstractProcessor<SpecificContext> {
@Override
public void process(SpecificContext context) {
// ... //
}
}
好的,很酷:处理器可以声明他们需要的 Context 类型,并且他们可以假设正确的类型将被传递到 process() 而不进行强制转换。
现在,我有一个 Dispatcher 类,它拥有字符串到处理器的映射:
class Dispatcher<C extends Context> {
Map<String, AbstractProcessor<? super C>> processorMap = new HashMap<String, AbstractProcessor<? super C>>();
public void registerProcessor(String name, AbstractProcessor<? super C> processor) {
processorMap.put(name, processor);
}
public void dispatch(String name, C context) {
processorMap.get(name).process(context);
}
}
好的,到目前为止一切顺利!我可以为特定类型的 Context 创建一个 Dispatcher,然后注册一批处理器,这些处理器可能期望该 Context 类型的任何抽象。
现在,问题来了:我希望抽象的 Context 类型拥有 Dispatcher,并且派生的 Context 类型应该能够注册额外的处理器。这是我能找到的最接近有效解决方案的方法,但它并不完全有效:
class Context<C extends Context> {
private final Dispatcher<C> dispatcher = new Dispatcher<C>();
public Context() {
// every context supports the BasicProcessor
registerProcessor("basic", new BasicProcessor());
}
protected void registerProcessor(String name, AbstractProcessor<? super C> processor) {
dispatcher.registerProcessor(name, processor);
}
public void runProcessor(String name) {
dispatcher.dispatch(name, this); // ERROR: can't cast Context<C> to C
}
}
// this is totally weird, but it was the only way I could find to provide the
// SpecificContext type to the base class for use in the generic type
class SpecificContext extends Context<SpecificContext> {
public SpecificContext() {
// the SpecificContext supports the SpecificProcessor
registerProcessor("specific", new SpecificProcessor());
}
}
问题是我需要在 Context 基类中声明一个泛型 Dispatcher,但我希望类型变量引用每个 Context 子类型的特定派生类型。如果不在每个 Context 子类中复制一些代码(特别是 Dispatcher 和 registerProcessor 方法的构造),我看不到这样做的方法。这就是我认为我真正想要的:
Dispatcher<MyRealClass> dispatcher = new Dispatcher<MyRealClass>();
有没有办法用声明类的 SUBCLASS 类型声明对象的泛型类型?
是的,我可以通过一些低风险的铸造来解决这个问题,所以这主要是一个学术问题......但我很想找到一个从上到下的解决方案!你能帮我吗?您将如何处理这种架构?
更新:
这是完整的源代码,已更新以纳入 Andrzej Doyle 的使用建议<C extends Context<C>>
;它仍然不起作用,因为Context<C> != C
:
class Context<C extends Context<C>> {
private final Dispatcher<C> dispatcher = new Dispatcher<C>();
public Context() {
// every context supports the BasicProcessor
registerProcessor("basic", new BasicProcessor());
}
protected void registerProcessor(String name, AbstractProcessor<? super C> processor) {
dispatcher.registerProcessor(name, processor);
}
public void runProcessor(String name) {
dispatcher.dispatch(name, this); // ERROR: can't cast Context<C> to C
}
}
// this is totally weird, but it was the only way I could find to provide the
// SpecificContext type to the base class for use in the generic type
class SpecificContext extends Context<SpecificContext> {
public SpecificContext() {
// the SpecificContext supports the SpecificProcessor
registerProcessor("specific", new SpecificProcessor());
}
}
abstract class AbstractProcessor<C extends Context<C>> {
public abstract void process(C context);
}
class BasicProcessor extends AbstractProcessor {
@Override
public void process(Context context) {
// ... //
}
}
class SpecificProcessor extends AbstractProcessor<SpecificContext> {
@Override
public void process(SpecificContext context) {
// ... //
}
}
class Dispatcher<C extends Context<C>> {
Map<String, AbstractProcessor<? super C>> processorMap = new HashMap<String, AbstractProcessor<? super C>>();
public void registerProcessor(String name, AbstractProcessor<? super C> processor) {
processorMap.put(name, processor);
}
public void dispatch(String name, C context) {
processorMap.get(name).process(context);
}
}