Search code examples
javaoopgenericsinterface

Interface Recursive Type Bound (self-bounding) example of implementation in Java


I've got an example of self-bound interface in Java:

public interface IAggregable<TElement extends IAggregable<TElement, TResult>, TResult> {

    TResult aggregate(TResult intermediateResult);
}

I have to somehow implement this interface for a new class. Can you Guys show me any example of how it can be done and how to call this aggregate method later?

Sorry for asking about something like that but this kind of "recursive" call in definition of this interface is confusing for me.

I've tried something like that:

public class CAgg<TResult> implements IAggregable<CAgg<TResult>,TResult> {
    @Override
    public TResult aggregate(TResult intermediateResult) {
        return null;
    }
}

but this implementation does not do anything. If we try with and for example make a "sum" of integers it shows error:

public class CAgg<TResult> implements IAggregable<CAgg<TResult>,TResult> {
    @Override
    public TResult aggregate(TResult intermediateResult) {
        return null;
    }
}

//and in main()
CAgg<Integer> a = new CAgg<>();
CAgg<Integer> b = new CAgg<>();

a.aggregate(b);

I will be glad for any help!


Solution

  • You don't need the additional generics in your implements statement, X implements I<X, T> already told the compiler what the generics for X were. So:

    class Main {
        interface Aggregateable<TElement extends Aggregateable<TElement, TResult>, TResult> {
            TResult aggregate(TResult intermediateResult);
        }
        
        class MyImplementation implements Aggregateable<MyImplementation, Integer> {
            // Obviously, you need more code here, because this does
            // nothing meaningful on its own, it needs whatever code
            // relies on having this interface implemented, and data
            // local to this class instance for it to aggregate.
            public Integer aggregate(Integer intermediateResult) {
                return intermediateResult;
            }
        }
    
        public static void main(String[] args) {
            new Main();
        }
        
        public Main() {
            MyImplementation m1 = new MyImplementation();
            // And obviously, this "does" nothing, but we don't care,
            // what we care about is that it compiles and runs without
            // errors and without throwing exceptions.
            System.out.println(m1.aggregate(5));
        }
    }
    

    But this is an esoteric pattern that—no matter what you're doing—is guaranteed the wrong pattern. It might works, but that doesn't make it good programming. It makes it "you'll have show that the algorithm you want to implement can only be implemented with this pattern". Which you can't.