문제

I have the following two interfaces:

/**
 * A marker interface to denote that an object implements a view on some other object.
 *
 * @param <T>   The type of object that is viewed
 */
public interface View<T extends Viewable<View<T>>> {

}

/**
 * An interface for objects that are viewable via a view.
 * 
 * @param <T>   The type of viewable object
 */
public interface Viewable<T extends View<?>> {
    public void addViewCallback(final T view);

    public void removeViewCallback(final T view);
}

I want to enforce the following:

  • The type parameter of the View (called (a)), should be a Viewable that views upon that view (a).
  • The type parameter of Viewable (called (b)), should be a View, which is viewable via that same viewable (b).

I think I got the bounds done for View, but how am I going to make them work for Viewable? What I got now compiles, but does not offer enough protection.

I cannot, as of now, formulate something that gets accepted which I do not want, I can however formulate what I do want, if that helps:

  • public class Hand implements Viewable<HandView>
  • public interface HandView extends View<Hand>
도움이 되었습니까?

해결책 2

As discussed with @SimonAndréForsberg, programming too defensively is often an unnecessary waste of time, and we should trust our own declarations.

However, for the fun of it, here is a solution to enforce all the restrictions you want on class/interfaces declarations at compile time.

Declarations

The View interface:

public interface View<T extends Viewable<T, ?>> {}
  • Here, we just want to ensure that the type parameter is a Viewable.

  • The second type argument does not need restriction, because the declaration of Viewable would not allow a Viewable<T,?> to exist if the ? were not itself a View<T>.

The Viewable interface:

public interface Viewable<T extends Viewable<T,?>, V extends View<T>> {
    public void addViewCallback(final V view);
    public void removeViewCallback(final V view);
}
  • Here, T needs to be declared as a Viewable because we use it as type parameter for View<T> at the end of the line.
  • Same as before, no restriction needed on ? because we say right after that the second type parameter needs to be a View<T> if the first type parameter is a T.

Usage

public class Hand implements Viewable<Hand, HandView> {
    @Override
    public void addViewCallback(HandView view) {}

    @Override
    public void removeViewCallback(HandView view) {}

}

public interface HandView extends View<Hand> {
}

다른 팁

As your View is a marker interface, and the fact that you as of now haven't formulated anything that gets accepted which you don't want, I question the use of the View interface entirely. You're putting an unnecessary restriction on yourself that leads to nothing but trouble.

Simplify!

/**
 * An interface for objects that are viewable via a view.
 * 
 * @param <T>   The type of viewable object
 */
public interface Viewable<T> {
    public void addViewCallback(final T view);

    public void removeViewCallback(final T view);
}

I managed to get it working, while taking all other answers here into consideration.

It seems like @Joffrey's answer got me started quite a bit, but then I realised that it is simply not possible this way, as method arguments cannot be covariant.

I also agree that it might be too much work, but that's why it is some experimental stuff, at work with time pressure I might indeed think twice before wasting time on this.

One of the possible solutions is:

/**
 * A marker interface to denote that an object implements a view on some other object.
 *
 * @param <T>   The type of object that is viewed
 */
public interface View<T extends Viewable<T, ? extends View<T>>> {

}

/**
 * An interface for objects that are viewable via a view.
 * 
 * @param <T>   The type of viewable object
 * @param <V>   The concrete view on the viewable object
 */
public interface Viewable<T extends Viewable<T, V>, V extends View<T>> {
    public void addViewCallback(final V view);

    public void removeViewCallback(final V view);
}

Example implementation:

public interface HandView extends View<Hand> {
    public void onCardAdded(final Card card);

    public void onCardPlayed(final int cardIndex);

    public void onCardsSwapped(final int cardIndexOne, final int cardIndexTwo);
}

public class Hand extends AbstractCollection<Card> implements Collection<Card>, Viewable<Hand, HandView> {
    private final List<HandView> views = new ArrayList<>();

    //...

    @Override
    public void addViewCallback(final HandView view) {
        views.add(Objects.requireNonNull(view));
    }

    @Override
    public void removeViewCallback(final HandView view) {
        views.remove(Objects.requireNonNull(view));
    }

    @Override
    public boolean add(final Card card) {
        Objects.requireNonNull(card);
        States.requireFalse(isFull(), "hand is full");
        list.add(card);
        views.forEach(view -> view.onCardAdded(card));
        return true;
    }

    //...
}

The only way this has ever worked for me was to specify both View and Viewable type parameters:

  • public interface View<VB extends Viewable<VB,T>, T extends View<VB,T>>
  • public interface Viewable<VB extends Viewable<VB,T>, T extends View<VB,T>>

Keep in mind that this will require you to create specific interfaces:

  • public interface Hand implements Viewable<Hand,HandView>
  • public interface HandView extends View<Hand,HandView>
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top