简体   繁体   English

需要java泛型声明的派生类

[英]Need derived class for java generics declaration

I've run into a sticky problem that I can't seem to solve with java generics. 我遇到了一个棘手的问题,我似乎无法解决java泛型问题。 This is a bit complicated, but I couldn't think of a simpler scenario to illustrate the problem... Here goes: 这有点复杂,但我想不出更简单的场景来说明问题......这里有:

I have a Processor class that requires a Context. 我有一个需要Context的Processor类。 There are different types of Context; 有不同类型的Context; most processors just need any abstract Context, but others require a specific subclass. 大多数处理器只需要任何抽象的Context,但其他处理器需要特定的子类。 Like this: 像这样:

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) {
        // ... //
    }
}

Ok, cool: Processors can declare the type of Context they need, and they can assume the right type will be passed into process() without casting. 好吧,很酷:处理器可以声明他们需要的Context类型,他们可以假设正确的类型将被传递到process()而不需要强制转换。

Now, I have a Dispatcher class that owns a mapping of Strings to Processors: 现在,我有一个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);
    }
}

Ok, so far so good! 好的,到目前为止一切顺利! I can create a Dispatcher for a specific type of Context, then register a batch of processors that may expect any abstraction of that Context type. 我可以为特定类型的Context创建一个Dispatcher,然后注册一批可能期望对该Context类型进行抽象的处理器。

Now, here's the problem: I want the abstract Context type to own the Dispatcher, and derived Context types should be able to register additional Processors. 现在,问题出在这里:我希望抽象的Context类型拥有Dispatcher,派生的Context类型应该能够注册其他的Processor。 Here's the closest I could find to a working solution, but it doesn't fully work: 这是我能找到的最接近工作解决方案的地方,但它不能完全发挥作用:

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());
    }
}

The problem is that I need to declare a generic Dispatcher in the base Context class, but I want the type-variable to refer to the specific derived type for each Context sub-type. 问题是我需要在基类Context类中声明一个泛型Dispatcher,但我希望type-variable引用每个Context子类型的特定派生类型。 I can't see a way to do this without duplicating some code in each Context subclass (specifically, the construction of the Dispatcher and the registerProcessor method). 我没有看到一种方法来做到这一点,而不重复每个Context子类中的一些代码(具体来说,Dispatcher和registerProcessor方法的构造)。 Here's what I think I really want: 这就是我认为我真正想要的:

Dispatcher<MyRealClass> dispatcher = new Dispatcher<MyRealClass>();

Is there a way to declare the generic type of an object with the type of the SUBCLASS of the declaring class? 有没有办法声明具有声明类的SUBCLASS类型的对象的泛型类型?

Yes, I can address this problem with a little bit of low-risk casting, so this is mostly an academic question... But I'd love to find a solution that just works top-to-bottom! 是的,我可以通过一些低风险的演员来解决这个问题,所以这主要是一个学术问题......但我很想找到一个从上到下的解决方案! Can you help? 你能帮我吗? How would you approach this architecture? 你会如何处理这种架构?


UPDATE : 更新

Here's the full source, updated to incorporate Andrzej Doyle's suggestion to use <C extends Context<C>> ; 这里是完整的源代码,更新了Andrzej Doyle的建议,使用<C extends Context<C>> ; it still doesn't work, because Context<C> != 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);
    }
}

It sounds like your problem is that you need the generics to refer to the specific exact type of the subclass, rather than inheriting the generic definition from the parents. 听起来你的问题是你需要泛型来引用子类的特定确切类型,而不是从父类继承泛型定义。 Try defining your Context class as 尝试将Context类定义为

class Context<C extends Context<C>>

Note the recursive use of the generic parameter - this is a bit hard to wrap one's head around, but it forces the subclass to refer to exactly itself . 注意泛型参数的递归使用 - 这有点难以包围,但它强制子类完全引用它自己 (To be honest I don't quite fully get this, but so long as you remember that it works, it works. For reference, the Enum class is defined in exactly the same way.) There's also a section in Angelika Langer's Generics FAQ that covers this . (说实话我不太充分得到这个,但只要你还记得它的工作原理,它的作品。作为参考, Enum类是在完全相同的方式定义。)还有在安格朗格的泛型常见问题一节这涵盖了这一点

This way the compiler gets more information about exactly what types are permissable, and should allow your case to compile without the superfluous casting. 通过这种方式,编译器可以获得有关哪些类型是允许的更多信息,并且应该允许您的案例在没有多余的转换的情况下进行编译。

UPDATE : Having thought about this a bit more, my above comments were along the right track but were not entirely on the money. 更新 :考虑到这一点,我的上述评论是在正确的轨道上,但并不完全是钱。 With self-recursive generic bounds, as above, you can never really use the actual class you define them on. 使用自递归泛型边界,如上所述,您永远不能真正使用您定义它们的实际类。 I'd actually never fully noticed this before, as by luck or judgement I'd apparently always used this in the right point of the class hierarchy. 我以前从来没有完全注意到这一点,因为通过运气或判断,我显然总是在类层次结构的正确位置使用它。

But I took the time to try and get your code to compile - and realised something. 但我花时间尝试让你的代码编译 - 并实现了一些东西。 The class with these bounds can never be referred to as itself, it can only ever be referred to in the context of a specific subclass. 具有这些边界的类永远不会被称为自身,它只能在特定子类的上下文中引用。 Consider the definition of BasicProcessor for example - Context appears ungenerified in the generic bounds for AbstractProcessor . 例如,考虑BasicProcessor的定义 - ContextAbstractProcessor的泛型边界中显得不同。 To prevent a raw type from appearing, it would be necessary to define the class as: 为了防止出现原始类型,有必要将类定义为:

class BasicProcessor extends AbstractProcessor<Context<Context<Context<...

This is avoided with subclasses because they incorporate the recursiveness in their definition: 子类可以避免这种情况,因为它们在定义中包含了递归:

class SpecificContext extends Context<SpecificContext>

I think this is fundamentally the problem here - the compiler cannot guarantee that C and Context<C> are the same types because it doesn't have the required special-casing logic to work out that the two are actually an equivalent type (which can only actually be the case when the wilcard chaining is infinite, since in any non-infinite sense the latter is always one level deeper than the first when expanded). 我认为这基本上是这里的问题 - 编译器不能保证CContext<C>是相同的类型,因为它没有必要的特殊外壳逻辑来解决这两个实际上是等价的类型(可以实际上只有当wilcard链接是无限的情况时,因为在任何非无限的意义上,后者总是比扩展时的第一级深一层。

So it's not a great conclusion, but I think in this case your cast is needed because the compiler is unable to derive the equivalence for itself otherwise. 所以这不是一个很好的结论,但我认为在这种情况下你的演员阵容是必需的,因为编译器无法为自己派生等价。 Alternatively, if you were using a concrete subclass of Context in a similar position the compiler is able to work it out and this would not be a problem. 另外,如果使用的具体子Context类似的位置,编译器能够做得出来,这将不会是一个问题。

If you do happen to find a way to get this working without casting or having to insert a dummy subclass then please report back - but I can't see a way to do that, that would work with the syntax and semantics available to Java's generics. 如果你碰巧找到了一种方法来使这个工作没有强制转换或不必插入一个虚拟子类,那么请报告 - 但我看不到这样做的方法,这将与Java的泛型可用的语法和语义一起使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM