pasaba por aqui
pasaba por aqui

Reputation: 3529

java: pairs of classes strongly related

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

Answers (1)

Leo Aso
Leo Aso

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

Related Questions