在课程级别或方法级别泛型之间进行选择

时间:2016-09-24 18:55:55

标签: java generics

如何在泛型类的类级别或方法级别定义之间进行选择?下面我有2个示例,其中包含针对某些问题的替代(正确)实现。我试图了解这些差异,以及为什么我会选择其中一个。

示例1: 给定一些处理搜索树的算法。为了帮助用户进行调试,算法中的某些状态会触发事件。用户可以实现监听这些事件的EventListener,例如一些记录器实用程序。

public Node<N extends Number>{ .. }

事件可以通过以下两种方式实现:

选项1 :(班级)

public class PruneNodeEvent<N extends Number> extends EventObject
{
    public final Node<N> node; //Node being pruned

    public PruneNodeEvent(Object source, Node<N> node)
    {
        super(source);
        this.node = node;
    }
}

选项2 :(方法级别)

public class PruneNodeEvent extends EventObject
{
    public final Node<? extends Number> node; //Node being pruned

    public PruneNodeEvent(Object source, Node<? extends Number> node)
    {
        super(source);
        this.node = node;
    }
}

应该考虑选择一个而不是另一个?

示例2: 给出一些听取Decisions的算法的抽象实现。每次做出新的决策时,算法都会更新其数据结构,但不会存储决策。由于这是一个抽象类(模板),我们不知道最终用户将如何使用此类。我们再次有两个选择:

选项1 :(班级)

public abstract class AbstractAlgorithm<T extends Data>
implements DecisionListener<T> {

    @Override
    public void makeDecision(Decision<T> d)
    {
    }

    @Override
    public void reverseDecision(Decision<T> d)
    {
    }
}

选项2 :(方法级别)

public abstract class AbstractAlgorithm implements DecisionListener
{
    @Override
    public void makeDecision(Decision<? extends Data> d) {
    }

    @Override
    public void reverseDecision(Decision<? extends Data> d) {
    }
}

应该考虑选择一个而不是另一个?

鼓励在设计通用类(ala Effective Java 2nd Edition)时包含有关Dos,Don'ts和考虑因素的好参考资料以支持您的答案。

1 个答案:

答案 0 :(得分:3)

  

示例1

这两者之间的主要区别在于您可以使用node

为了完整性,我们只需添加第三个选项:

public final Node<? super Number> node;

请记住首字母缩略词 PECS

  

Producer Extends,Consumer Super

  • 在第二个选项中,Node<? extends T> node制作者:您只能调用在其上生成T的方法。

    T instanceOfT = node.get();
    

    但您无法使用null以外的参数调用使用者方法:

    node.accept(null);         // OK.
    node.accept(instanceOfT);  // Compiler error.
    
  • 在第三个选项中,Node<? super T> node使用者:您只能在其上调用采取 a T的方法,例如

    node.accept(instanceOfT);
    

    但您无法调用生产者方法并将其结果分配给依赖于T的类型:

    Object obj = node.get();         // OK.
    List<?> list = node.getList();   // OK.
    T t = node.get();                // Compiler error.
    List<T> listT = node.getList();  // Compiler error.
    
  • 在第一个选项中,Node<T> node 都是生产者和消费者,因为它是选项2和3的交集(同时为{{1 }}和<? extends T><? super T>):

    <T>

因此,3个选项之间的选择取决于您需要对T t = node.get(); // OK. node.accept(t); // OK.

执行的操作
  • 如果您需要它来生成node,请选择T<T>;
  • 如果您需要使用<? extends T>,请选择T<T>;
  • 如果您需要它来生成消费<? super T>,请选择T
  

示例2

如果您只想为<T>的特定参数类型调用方法,请使用第一个,例如

Decision

如果您希望能够为具有相同AbstractAlgorithm<StringData> foo = new AbstractAlgorithm<>(); foo.makeDecision(new Decision<StringData>()); // OK. foo.makeDecision(new Decision<IntegerData>()); // Compiler error. 的任何类型调用方法,请使用第二个,例如

AbstractAlgorithm