Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why Does the following code with Cyclic Generics not compile?

Following is my code

class A<B2 extends B, A2 extends A<B2, A2>> {
    C<B2, A2> c;

    void test() {
        c.acceptParameterOfTypeA(this);
    }

}

class B {

}

class C<B2 extends B, A2 extends A<B2, A2>> {
    void acceptParameterOfTypeA(A2 a) {

    }
}

The error occurs at c.acceptParameterOfTypeA(this);.

The error is

The method acceptParameterOfTypeA(A2) in the type C is not applicable for the arguments (A)

From what I see, the method acceptParameterOfTypeA expects a parameter of type A, and this at the line giving the error is of type A.

What am I doing wrong? How to fix this problem?

If its important, I'm using Java8

like image 481
Can't Tell Avatar asked Jul 29 '15 16:07

Can't Tell


2 Answers

I will again rename your classes, so that everything is more readable. So, let's have:

public class First<T extends Second, U extends First<T, U>> {
    Third<T, U> c;

    void test() {
        c.acceptParameterOfTypeA(this);
    }

}

class Second {

}

public class Third<X extends Second, Y extends First<X, Y>> {
    void acceptParameterOfTypeA(Y a) {

    }
}

From the definition of the c member (Third<T, U>), we can conclude that c will expose a method with this signature:

void acceptParameterOfTypeA(U a) { .. }

What is U? U is a sub-type of First<T, U>.

But if U can be substituted with First after type-erasure, this will mean that First extends First<T, First>, which is not true, because U stands for sub-type of First, which is parameterized with some concrete sub-types of Second and First.

In order to get to U, you can apply the so-called Get This approach.

First, since you need U, which is a sub-type of First, but can't get it from First, you can introduce an abstract method that returns it:

abstract class First<T extends Second, U extends First<T, U>> {
    Third<T, U> c;

    void test() {
        c.acceptParameterOfTypeA(getU());
    }

    abstract U getU();

}

Then, implement a sample sub-class of First, called Fourth, which extends First with some concrete types for T and U, for example:

class Fourth extends First<Second, Fourth> {
    Fourth getU() {
        return this;
    }
}

In the getU() method, just do return this; as this will return the correct substitute for U in the super-class.

More info:

  • What is the "getThis" trick?
  • Strategy Pattern with Generics
like image 89
Konstantin Yovkov Avatar answered Oct 15 '22 16:10

Konstantin Yovkov


Simply put, c.acceptParameterOfTypeA() accepts A2. this has type A<B2, A2>, which is not known to extend A2. It's only known that A2 extends A<B2, A2>.

like image 43
newacct Avatar answered Oct 15 '22 15:10

newacct