How to ensure this is the generic type referenced in my class signature?

Viewed 251

Suppose we have three classes - AbstractMessage, AbstractEngine, and AbstractAction. These three classes all reference each other in a generic fashion so each Engine has a corresponding message and action and you can refer to them directly in the code.

public class MyMessage<M extends AbstractMessage<M,E,A>, E extends AbstractEngine<M,E,A>, A extends AbstractAction<M,E,A>> {

This is working fine, but when I attempt to enforce behavior at the highest level I run into some problems. My AbstractAction class has an applyTo method defined thusly:

protected abstract M applyTo(E engine, Object guarantee);

and my AbstractEngine class has this

private final M apply(A action) {
    return action.apply(this, this.guarantee);
}

And it is on this line that it balks - complaining that:

The method applyTo(E, Object) in the type AbstractAction<M,E,A> is not
applicable for the arguments (AbstractEngine<M,E,A>, Object)

Now the reason for this is clear - the E in question might be some OTHER AbstractEngine and there's no way to know if the subclass we're calling this from is actually an E.

My question is, how can I say for certainty that if you're going to class MyEngine extends AbstractEngine<M...,E...,A...> that MyEngine is MUST be an E? And to have this certainty baked into AbstractEngine?

Here is a small example that illustrates the problem.

class EngineExample {

    static abstract class AbEng<A extends AbAct<A,M,E>, M extends AbMes<A,M,E>, E extends AbEng<A,M,E>> {

        final M func(A act) {
            return act.apply(this); // compile error here
        }

    }

    static abstract class AbMes<A extends AbAct<A,M,E>, M extends AbMes<A,M,E>, E extends AbEng<A,M,E>> {

    }

    static abstract class AbAct<A extends AbAct<A,M,E>, M extends AbMes<A,M,E>, E extends AbEng<A,M,E>> {

        abstract void apply(E e);

    }

    static class RealEng extends AbEng<RealAct, RealMes, RealEng> {

    }

    static class RealMes extends AbMes<RealAct, RealMes, RealEng> {

    }

    static class RealAct extends AbAct<RealAct, RealMes, RealEng> {

        void apply(RealEng eng) {
            System.out.println("applied!");
        }
    }

}
2 Answers
Related