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
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
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>
.
Based on kocko
's answer, the original question had the same solution:
public class Main {
abstract class A<A2 extends A<A2, B2>, B2 extends B<A2, B2>> {
B2 b;
void test() {
b.testMethod(getThis()); //getThis() instead of this;
}
abstract A2 getThis();
}
class B<A2 extends A<A2, B2>, B2 extends B<A2, B2>> {
void testMethod(A2 a) {
}
}
public void execute() {
}
public static void main(String[] args) {
Main main = new Main();
main.execute();
}
}
We can simplify it by removing the B
part which doesn't contribute to the problem -
class A<T extends A<T>>
{
void test(C<T> c)
{
c.acceptParameterOfTypeA(this); // ERROR
}
}
class C<T extends A<T>>
{
void acceptParameterOfTypeA(T a) {}
}
this
type is A<T>
; and the question is whether A<T> <: T
, which is false.
What we really want here is "self type", so that this
type is T
. We don't have that in Java.
Usually we use T extends A<T>
for "self type"; but it is flawed and inadequate in some use cases.
One remedy for that is T getThis()
, as kocko mentioned.
You could simply do a brute cast (T)this
, which is obviously correct by the intention of T
.
My preferred approach is to simply omit the bound of T
, and rename it to This
to indicate the purpose of the type variable. Casting (This)this
looks obviously correct. See my other post. That approach usually works; but it doesn't work here, since C
would need This
to have the bound A<This>
. The deeper problem is A
and C
depends on each other, which might be redesigned.