jos*_*hng 6 java generics abstraction
我遇到了一个棘手的问题,我似乎无法解决java泛型问题.这有点复杂,但我想不出更简单的场景来说明问题......这里有:
我有一个需要Context的Processor类.有不同类型的Context; 大多数处理器只需要任何抽象的Context,但其他处理器需要特定的子类.像这样:
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) {
// ... //
}
}
Run Code Online (Sandbox Code Playgroud)
好吧,很酷:处理器可以声明他们需要的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);
}
}
Run Code Online (Sandbox Code Playgroud)
好的,到目前为止一切顺利!我可以为特定类型的Context创建一个Dispatcher,然后注册一批处理器,这些处理器可能期望该Context类型的任何抽象.
现在,问题出在这里:我希望抽象的Context类型拥有Dispatcher,派生的Context类型应该能够注册其他的Processor.这是我能找到的最接近工作解决方案的地方,但它不能完全发挥作用:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
问题是我需要在基类Context类中声明一个泛型Dispatcher,但我希望type-variable引用每个Context子类型的特定派生类型.我没有看到一种方法来做到这一点,而不重复每个Context子类中的一些代码(具体来说,Dispatcher和registerProcessor方法的构造).这就是我认为我真正想要的:
Dispatcher<MyRealClass> dispatcher = new Dispatcher<MyRealClass>();
Run Code Online (Sandbox Code Playgroud)
有没有办法声明具有声明类的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);
}
}
Run Code Online (Sandbox Code Playgroud)
听起来您的问题是您需要泛型来引用子类的特定确切类型,而不是从父类继承泛型定义。尝试将您的 Context 类定义为
class Context<C extends Context<C>>
Run Code Online (Sandbox Code Playgroud)
注意泛型参数的递归使用——这有点难以理解,但它迫使子类准确地引用它自己。(说实话,我不太完全明白这一点,但只要你记住它有效,它就有效。作为参考,该类的Enum
定义方式完全相同。)Angelika Langer 的泛型常见问题解答中还有一个部分涵盖了这一点。
通过这种方式,编译器可以获得有关允许的类型的更多信息,并且应该允许您的案例在没有多余转换的情况下进行编译。
更新:再考虑一下这个问题,我的上述评论是正确的,但并不完全是为了钱。如上所述,使用自递归泛型边界,您永远无法真正使用定义它们的实际类。事实上,我以前从未完全注意到这一点,因为幸运或判断,我显然总是在类层次结构的正确位置使用它。
但我花时间尝试编译你的代码 - 并意识到了一些事情。具有这些边界的类永远不能被引用为自身,它只能在特定子类的上下文中被引用。BasicProcessor
考虑例如 -的定义Context
在 的通用范围内似乎未泛化AbstractProcessor
。为了防止出现原始类型,有必要将类定义为:
class BasicProcessor extends AbstractProcessor<Context<Context<Context<...
Run Code Online (Sandbox Code Playgroud)
子类可以避免这种情况,因为它们在定义中包含了递归性:
class SpecificContext extends Context<SpecificContext>
Run Code Online (Sandbox Code Playgroud)
我认为这从根本上就是这里的问题 - 编译器不能保证C
和Context<C>
是相同的类型,因为它没有所需的特殊情况逻辑来计算出这两个实际上是等效类型(实际上只能是这种情况)当通配符链接是无限的时,因为在任何非无限意义上,后者在扩展时总是比第一个更深一层)。
所以这不是一个很好的结论,但我认为在这种情况下你需要进行强制转换,因为否则编译器无法导出自身的等价性。或者,如果您在类似的位置使用具体的子类,Context
编译器能够解决这个问题,这不会成为问题。
如果您确实找到了一种无需强制转换或无需插入虚拟子类即可实现此功能的方法,请报告回来 - 但我看不到一种方法可以做到这一点,这可以与 Java 泛型可用的语法和语义一起使用。
归档时间: |
|
查看次数: |
2629 次 |
最近记录: |