Required derived class to declare generics java

I ran into a sticky problem that I cannot solve with java generics. This is a bit complicated, but I could not come up with a simpler scenario to illustrate the problem ... Like this:

I have a processor class that needs context. There are various types of context; most processors need some kind of abstract context, but others require a certain subclass. 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 that the correct type will be passed to process () without casting.

Now I have a Dispatcher class that owns string matching for processors:

 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, and then register a processor package that can expect any abstraction of this type of 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. Here is the closest I can find for a working solution, but it does not work fully:

 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 Context base class, but I want the type variable to refer to a specific derived type for each Context subtype. I don’t see a way to do this without duplicating some code in each subclass of Context (in particular, when creating a Dispatcher and registerProcessor method). Here is what I think I really want:

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

Is there a way to declare a generic object type with a SUBCLASS type of a declaring class?

Yes, I can solve this problem with a bit of low-risk casting, so this is basically an academic question ... But I would like to find a solution that just works from top to bottom! You can help? How do you approach this architecture?


UPDATE

Here's the full source, updated to include Andrzej Doyle's suggestion to use <C extends Context<C>> ; it still does not work, because 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); } } 
+6
java generics abstraction
source share
1 answer

It seems your problem is that you need generalizations to refer to a specific exact type of subclass, rather than inheriting a common definition from parents. Try defining the Context class as

 class Context<C extends Context<C>> 

Pay attention to the recursive use of the general parameter - it is a little difficult to wrap around yourself, but it forces the subclass to refer exactly to itself . (Honestly, I don’t quite understand this, but as long as you remember that it works, it works. For reference, the Enum class is defined in exactly the same way.) There is also a section in Angelika Langer General questions about what this covers .

Thus, the compiler gets more information about which types are valid, and should allow your case to compile without unnecessary casting.

UPDATE : Having thought about this a bit more, my comments were on the right track, but were not completely for money. With self-recursive general constraints, as above, you can never use the actual class on which you define them. I have never really noticed this before, since by luck or judgment I apparently always used this on the right side of the class hierarchy.

But I took the time to try to compile your code - and I understood something. A class with these boundaries can never be referred to as itself, it can only ever be referenced in the context of a particular subclass. Consider the definition of BasicProcessor , for example, - Context appears non-generated in general evaluations for AbstractProcessor . To prevent the appearance of a raw type, you must define the class as:

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

This can be avoided by using subclasses, as they include recursiveness in their definition:

 class SpecificContext extends Context<SpecificContext> 

I think this is basically a problem here: the compiler cannot guarantee that C and Context<C> are the same types, because it does not have the required special case logic to understand that these two are actually equivalent to type (which can only be in in the case where the wilcard chain is infinite, since in any not infinite sense the latter will always be one level deeper than the first when it expands).

So, this is not an excellent conclusion, but I think that in this case your throw is necessary, because the compiler cannot get equivalence for itself otherwise. Alternatively, if you used a specific subclass of Context in a similar position, the compiler will be able to process it, and this will not be a problem.

If you are lucky enough to find a way to make this work without casting or insert a dummy subclass, then please report back - but I see no way to do this, which will work with syntax and semantics for Java generics.

+2
source share

All Articles