有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

我有以下两个接口:

/**
 * 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))的类型参数应该是查看该视图(a)的Viewable
  • Viewable(称为(b))的类型参数应该是aView,它可以通过相同的可视对象(b)进行查看

我想我已经为View完成了边界,但是我要如何使它们为Viewable工作呢?我现在得到的东西可以编译,但不能提供足够的保护

到目前为止,我无法制定一些我不想要的被接受的东西,但是我可以制定我想要的东西,如果这有帮助的话:

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

共 (4) 个答案

  1. # 1 楼答案

    这对我来说唯一有效的方法是同时指定视图和可查看类型参数:

    • 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>
  2. # 2 楼答案

    由于您的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);
    }
    
  3. # 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;
        }
    
        //...
    }
    
  4. # 4 楼答案

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

    然而,为了好玩,这里有一个解决方案,可以在编译时强制执行您想要的对类/接口声明的所有限制

    声明

    View接口:

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

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

    {}接口:

    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>的类型参数
    • 与前面一样,不需要对?进行任何限制,因为在这之后,如果第一个类型参数是T,则第二个类型参数需要是View<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> {
    }