Reputation: 3529
I've pairs of classes related one to the other:
Msg1 <-> Data1
Msg2 <-> Data2
...
Where all "DataX" extends the super class "Data" and "MsgX" extends the class "Msg". These super classes are currently (simplified):
abstract class Data< MSG extends Msg<? extends Data<MSG>>> {
MSG msg;
void modify( MSG msg ) {
this.msg = msg;
};
}
and:
abstract class Msg< DATA extends Data<? extends Msg<DATA>>> {
abstract DATA createData();
void modifyData( DATA data ) {
data.modify( this );
};
}
However, compilation of these two classes, Data and Msg, fails with following error at statement "data.modify( this );":
Msg.java:5: error: method modify in class Data<MSG> cannot be applied to given types;
data.modify( this );
^
required: CAP#1
found: Msg<DATA>
reason: argument mismatch; Msg<DATA> cannot be converted to CAP#1
where DATA,MSG are type-variables:
DATA extends Data<? extends Msg<DATA>> declared in class Msg
MSG extends Msg<? extends Data<MSG>> declared in class Data
where CAP#1 is a fresh type-variable:
CAP#1 extends Msg<DATA> from capture of ? extends Msg<DATA>
1 error
Any suggestion ? I've tried tenths of variants, adding and removing "?", "extends", ... but all they fails in someway.
Upvotes: 0
Views: 47
Reputation: 12473
Java be like that sometimes 🤷. Basically what you need to do is define a concrete type parameter that refers back to the class being declared, instead of using a wildcard. What I mean is, instead of
abstract class Data<M extends Msg<? extends Data<M>>> {}
abstract class Msg<D extends Data<? extends Msg<D>>> {}
declare it as
abstract class Data<D extends Data<D, M>, M extends Msg<M, D>>
abstract class Msg<M extends Msg<M, D>, D extends Data<D, M>>
To be precise, define the base classes like this
abstract class Data<D extends Data<D, M>, M extends Msg<M, D>> {
M msg;
void modify( M msg ) {
this.msg = msg;
};
}
abstract class Msg<M extends Msg<M, D>, D extends Data<D, M>> {
abstract D createData();
void modifyData( D data ) {
data.modify( (M) this ); // <- you still need to cast `this` though
};
}
and then you can declare subclasses like this:
class FooData extends Data<FooData, FooMsg> {
}
class FooMsg extends Msg<FooMsg, FooData> {
@Override FooData createData() {
return new FooData();
}
}
Upvotes: 2