Linking two common types

I have a question about generics. I have the following interfaces:

public interface InterfaceA<B extends InterfaceA.InterfaceB> {

    public interface InterfaceB<A extends InterfaceA> {

        void setA(A a);
    }
}

      

And the following abstract implementation InterfaceA

:

public abstract class AImplOne<B extends InterfaceA.InterfaceB> implements InterfaceA<B> {

    private final B b;

    public AImplOne(B b) {
        this.b = b;
        b.setA(this); // <-- Unchecked call...
    }
}

      

It's clear to me that the call b.setA(this)

is unchecked - but I don't like it, so I tried a second abstract implementation:

public abstract class AImplTwo<A extends InterfaceA, B extends InterfaceA.InterfaceB<A>> implements InterfaceA<B> {

    private final B b;

    public AImplTwo(B b) {
        this.b = b;
        b.setA((A)this); // <-- Unchecked cast
    }
}

      

And again, it's clear to me that the challenge b.setA((A)this)

is to uncheck the box.

But how can this be implemented or redone to get rid of the unverified code?

+3


source to share


1 answer


Actually you have a mutually recursive generic definition that you break down using raw types: in

b.setA((A)this); // <- Unchecked cast

      

this

has a type InterfaceA<? extends InterfaceA.InterfaceB<? extends InterfaceA>>

but must have a type InterfaceA<? extends InterfaceA.InterfaceB<? extends InterfaceA<? extends InterfaceA.InterfaceB<...>>>>

. Instead, you will have to use

public interface InterfaceA<B extends InterfaceA.InterfaceB<?>> {

    public interface InterfaceB<A extends InterfaceA<B>> { //<- cannot make a static reference to the non-static type B

        void setA(A a);
    }
}

      

but you cannot use B

which is non-stationary in a static interface declaration (interface declarations are always static).


For details, one more attempt: using an alternative

public interface InterfaceA<B extends InterfaceA.InterfaceB<?>> {

    public interface InterfaceB<A extends InterfaceA<? extends InterfaceA.InterfaceB<?>>> {

        void setA(A a);
    }
}


abstract class AImplTwo<B extends InterfaceA.InterfaceB<A>, A extends InterfaceA<B>> implements InterfaceA<B> {

    private final B b;

    public AImplTwo(B b) {
        this.b = b;
        b.setA((A)this); // <-- Unchecked cast
    }
}

      

again invokes an uncontrolled selection since the nested type parameter InterfaceA

in interface InterfaceB<A extends InterfaceA<? extends InterfaceA.InterfaceB<?>>>

is now an arbitrary subclass again InterfaceA.InterfaceB<?>

.




Update since you asked for general design:

I would think of an interface (in fact, interfaces in general) as an abstraction from a specific implementation: you need InterfaceB, not its implementation details, in your InterfaceA implementation. Think of InterfaceB as a contract and you don't need an implementation. Hence, there is no need to link the InterfaceA implementation to the InterfaceB implementation:

public interface InterfaceA {

    public interface InterfaceB {

        void setA(InterfaceA a);
    }
}

      

Only if, for reasons I can't see, you want to have the same type for all InterfaceB interfaces you use, do you need generics. And vice versa for InterfaceA. In the generic example above, you can at least fix the types for InterfaceA and InterfaceB and only need to dynamically assert that AB and BA are the same.

Showing that there is no type-checked solution type in Java, but perhaps it becomes plausible in the following example, which would be a solution if Java allowed the combination of extends and super:

public interface A<TB extends A.B<?>> {

    public interface B<TA extends A<? extends A.B<?>>> {

        void setA(TA a);
    }
}


class AImplTwo<TB extends A.B<TA>, TA extends AImplTwo<TB, TA> super AImplTwo<TB, TA>> implements A<TB> {

    private final TB b;

    public AImplTwo(TB b) {
        this.b = b;
        b.setA((TA)this);
    }
}

      

... think about it, the Pluggable Type System, which adds additional typing to Java, allows this combination of extends and super to be used and so may offer a solution to your problem. But I find it too complicated for what you get, and will either stick to a simple use of generic-free interfaces or an unsupervised cast.

+2


source







All Articles