Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this type not a valid substitute for the type parameter?

Tags:

java

generics

I'm experimenting with using generics to support a configurable structure of delegating objects (decorators, wrappers). I want to build a chain of delegators that implements a target interface as well as a generic delegator interface.

I have this outline:

class Test {
    static interface Delegator<T> {}

    static class DelegatorChain<T extends Delegator<T>> {}

    static interface Foo {}

    static class FooDelegator implements Delegator<Foo>, Foo {}

    public static void main(String[] args) {
        DelegatorChain<FooDelegator> chain = new DelegatorChain<FooDelegator>();
    }
}

But, when trying to instantiate the chain variable, compiler complains:

Bound mismatch: The type Test.FooDelegator is not a valid substitute for the bounded parameter <T extends Test.Delegator<T>> of the type Test.DelegatorChain<T>

I admit that generics is like magic to me, but I can somehow acknowledge that FooDelegator is not a Foo that extends Delegator<Foo>, it simply implements both interfaces.

Given that it's clear what I want to accomplish, is there anything I can do w.r.t. generics to fix it, or am I just better of forgetting about it?

like image 221
forty-two Avatar asked Aug 11 '11 21:08

forty-two


2 Answers

Under your definition, a Delegator is a Delegator of itself (like Comparable is for example), however it seems the intention is that Delegator is a Delegator of a super class. Luckily, generics has a way of expressing this:

static class DelegatorChain<T extends Delegator<? super T>> {}

This says that the "Delagator type must be a super class of T". With this change, the rest of your original code compiles:

static interface Delegator<T> {}
static class DelegatorChain<T extends Delegator<? super T>> {}
static interface Foo {}
static class FooDelegator implements Delegator<Foo>, Foo {}

public static void main(String[] args) {
    DelegatorChain<FooDelegator> chain = new DelegatorChain<FooDelegator>();
}

Also, anytime you use a generic super bound, your code looks really cool :)



Note: This following was originally the "first option" in the question.
There is another way to get your code to compile, but it is inferior because it loses the connect between the Delegator type and what it's delegating from:

// Not recommended, but will allow compile:
static class FooDelegator implements Delegator<FooDelegator>, Foo {} 
// However, this also compiles :(
static class FooDelegator implements Delegator<FooDelegator>, Bar {} 
like image 160
Bohemian Avatar answered Oct 11 '22 23:10

Bohemian


It looks like this is what you are trying to do.

static interface Delegator<T> {
    }

    static class DelegatorChain<T extends Delegator<C>, C> {
    }

    static interface Foo {
    }

    static class FooDelegator implements Delegator<Foo>, Foo {
    }

    public static void main(String[] args) {
        DelegatorChain<FooDelegator, Foo> chain = new DelegatorChain<FooDelegator, Foo>();
    }

Your initial example does not compile because the types are not correct. The Generic type in DelegatorChain is a "FooDelegator" but the generic type required in the Delegator is "Foo". You'll need the extra generic type parameter that i provided in my answer to make it work as you intended.

You could also leave the constraint off entirely on DelegatorChain i.e. DelegatorChain.

like image 40
ScArcher2 Avatar answered Oct 11 '22 21:10

ScArcher2