Home > Software engineering >  How to handle polymorphism with interfaces in bounded type parameters? (Java)
How to handle polymorphism with interfaces in bounded type parameters? (Java)

Time:08-06

I've got a class hierarchy of the following kind:

class Declarations {

    interface Function<I, O> {
        interface BinaryFunction<I> extends Function<I, Boolean> {}
    }

    static abstract class FunctionImpl<I, O> implements Function<I, O> {

        static class BinaryFunctionImpl<I>
               extends FunctionImpl<I, Boolean>
               implements Function.BinaryFunction<I> {}
    }

    interface Optimizer<I, O, F extends Function<I, O>> {

        interface BinaryOptimizer<I>
        extends Optimizer<I, Boolean, Function.BinaryFunction<I>> {}
    }

    static abstract class AbstractOptimizer<I, O, F extends Function<I, O>>
                    implements Optimizer<I, O, F> {}

    static abstract class OptimizerImpl<I, O, F extends FunctionImpl<I, O>>
                    extends AbstractOptimizer<I, O, F> {

        static class BinaryOptimizerImpl<I>
               extends OptimizerImpl<I, Boolean, FunctionImpl.BinaryFunctionImpl<I>>
               implements BinaryOptimizer<I> {}
    }
}

The compiler stumbles upon the declaration of BinaryOptimizerImpl<I> and says:

'Optimizer' cannot be inherited with different type arguments: 'BinaryFunctionImpl<I>' and 'BinaryFunction<I>'

But I don't understand the issue, as BinaryFunctionImpl<I> implements BinaryFunction<I> and both are passed the same parameter. So these two types should be compatible from my understanding. If BinaryFunction<I> was a class rather than an interface, I'd understand that constrained type parameters don't allow for instantiating them with subclasses unless declared with ? extends Type. But for interfaces, this wouldn't make sense, as you can never instantiate an interface directly.

All of the shown types are already in active use within the surrounding codebase, except for OptimizerImpl/BinaryOptimizerImpl, which must provide the respective Optimizer interface. Also note that the inheritance from AbstractOptimizer in OptimizerImpl cannot be resolved by integrating it into OptimizerImpl, as it is also inherited from elsewhere (I know, the naming isn't ideal, sorry!).

How to make this compile with minimal impact on the existing class hierarchy, i.e. changing only the lowest-possible (most specific) type layer, to reduce the impact on the existing codebase? Is there a way to make this compile by only changing the declaration of BinaryOptimizerImpl? (Leaving away the obvious non-solution of not making it implement BinaryOptimizer)

I hope my question is comprehensible, I had to abstract a lot. I've done quite some research on the semantics of Generics in Java, but there's a large variety of mistakes to be made and I couldn't find any explanation for this one.

CodePudding user response:

I believe F in Optimizer cannot be both BinaryFunctionImpl and BinaryFunction at the same time, it must resolve to a single type.

What about

    interface BinaryOptimizer<I, G extends Function.BinaryFunction<I>>
            extends Optimizer<I, Boolean, G> {}

    static class BinaryOptimizerImpl<I>
            extends OptimizerImpl<I, Boolean, FunctionImpl.BinaryFunctionImpl<I>>
            implements BinaryOptimizer<I, FunctionImpl.BinaryFunctionImpl<I>> {}

CodePudding user response:

So I found out that in my case, it is actually sufficient for BinaryOptimizerImpl to implement Optimizer<I, Boolean, FunctionImpl.BinaryFunctionImpl<I>>, which it already becomes through inheritance. Contrary to my initial assumption, its callers do not require it to be a BinaryOptimizer. Leaving away its declaration as BinaryOptimizer is the minimal-impact solution for my codebase.

However, leaving aside external complications and judging only from a design perspective, I do believe that the solution presented by upeterz is more clean with regards to the class hierarchy presented in my question, which is why I consider their answer as preferable.

  • Related