几乎循环类型绑定的递归类型参数

时间:2014-04-29 12:16:19

标签: java generics bounded-wildcard

我有以下两个接口:

/**
 * 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);
}

我想强制执行以下操作:

  • View的类型参数(称为(a))应该是Viewable,可以查看该视图(a)。
  • Viewable的类型参数(称为(b))应为View,可通过相同的可见(b)查看。

我认为我完成了View的界限,但是我如何让它们为Viewable工作呢?我现在得到的是编译,但没有提供足够的保护。

我现在不能制定一些我不想要的东西,但是我可以制定我想要的东西,如果有帮助的话:

  • public class Hand implements Viewable<HandView>
  • public interface HandView extends View<Hand>

4 个答案:

答案 0 :(得分:7)

由于您的View是一个标记界面,而且您现在还没有制定任何被接受的内容,而您不要想要,我完全质疑View接口的使用。你对自己施加了不必要的限制,只能带来麻烦。

简化!

/**
 * 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);
}

答案 1 :(得分:5)

正如@SimonAndréForsberg所讨论的那样,编程过于防守往往是不必要的浪费时间,我们应该相信我们自己的声明。

但是,为了它的乐趣,这里有一个解决方案,可以在编译时强制执行对类/接口声明的所有限制。

声明

View界面:

public interface View<T extends Viewable<T, ?>> {}
  • 在这里,我们只想确保类型参数为Viewable

  • 第二种类型参数不需要限制,因为如果Viewable本身不是Viewable<T,?>?的声明将不允许View<T>存在}。

Viewable界面:

public interface Viewable<T extends Viewable<T,?>, V extends View<T>> {
    public void addViewCallback(final V view);
    public void removeViewCallback(final V view);
}
  • 此处,T需要声明为Viewable,因为我们将其用作行末View<T>的类型参数。
  • 与以前相同,?不需要限制,因为我们之后说如果第一个类型参数是View<T>,则第二个类型参数必须是T

用法

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

答案 2 :(得分:3)

我设法让它工作,同时考虑所有其他答案。

似乎@Joffrey's answer让我开始了很多,但后来我意识到这种方式根本不可能,因为方法参数不能协变

我也同意这可能是太多的工作,但这就是为什么它是一些实验性的东西,在工作时间压力我可能会在浪费时间之前三思而后行。

可能的解决方案之一是:

/**
 * 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);
}

示例实施:

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

    //...
}

答案 3 :(得分:2)

这对我有用的唯一方法是指定View和Viewable类型参数:

  • 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>>

请记住,这需要您创建特定的界面:

  • public interface Hand implements Viewable<Hand,HandView>
  • public interface HandView extends View<Hand,HandView>