9

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>
skiwi
  • 66,971
  • 31
  • 131
  • 216
  • 1
    Could you formulate an instantiation currently allowed that you don't want to allow? – Joffrey Apr 29 '14 at 12:25
  • @Joffrey Unfortunately no, but I have formulated what I want to have working though, I am worried that `Deck implements Viewable`, where `Deck` and `Field` are *not* related, would be possible. – skiwi Apr 29 '14 at 12:28
  • I see. The current interfaces' definitions are too restricted to allow what you want, but you don't manage to broaden them without allowing `Deck implements Viewable`, is that your problem? – Joffrey Apr 29 '14 at 12:31
  • 1
    *A marker interface to denote that an object implements a view on some other object.* It doesn't seem to be *some other object* but the object itself. That's what bugs me. – Joffrey Apr 29 '14 at 12:33
  • 1
    Could you please elaborate on the use case itself? It seems that, as @SimonAndréForsberg pointed out, you might not need that much complexity. – Joffrey Apr 29 '14 at 12:39

4 Answers4

7

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);
}
Community
  • 1
  • 1
Simon Forsberg
  • 13,086
  • 10
  • 64
  • 108
  • +1 for the simplification suggestion. However, I think it is simplified too much here, since the OP wants some restriction on the type passed to the methods. – Joffrey Apr 29 '14 at 12:51
  • @Joffrey I think the OP is putting to many restrictions on himself. I see no need to restrict this at all. It's up to implementations of the interface to pick the type that they choose. – Simon Forsberg Apr 29 '14 at 12:57
  • 1
    Very true, programming too defensively is sometimes an unnecessary waste of time. The OP should probably trust his own classes delcarations :) – Joffrey Apr 29 '14 at 13:03
5

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> {
}
Joffrey
  • 32,348
  • 6
  • 68
  • 100
3

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;
    }

    //...
}
Community
  • 1
  • 1
skiwi
  • 66,971
  • 31
  • 131
  • 216
  • 1
    I don't think you need the extra restriction on the ? for the delcaration of the `View` interface. Indeed, the `Viewable` declaration already limits the second type argument. – Joffrey Apr 29 '14 at 13:29
2

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>
Tassos Bassoukos
  • 16,017
  • 2
  • 36
  • 40
  • You might be on to something here, but this does not compile at the moment, which makes it hard to know what you're on to. `Incorrect number of arguments for type Viewable; it cannot be parameterized with arguments >` (Error x2) – Simon Forsberg Apr 29 '14 at 12:52
  • Unfortunately it still doesn't compile, `Incorrect number of arguments for type View; it cannot be parameterized with arguments ` here: `VB extends Viewable<**View**>` – Simon Forsberg Apr 29 '14 at 13:22
  • Gah, that's what happens when I try to write code without an IDE... refixed(2). – Tassos Bassoukos Apr 29 '14 at 13:25
  • @TassosBassoukos So hard without IDE indeed ^^ Especially for all these interlaced generics! – Joffrey Apr 29 '14 at 13:26