2016-09-24 83 views
1

如何在泛型类型级别或方法级别定义之间进行选择?下面我有两个例子,用于解决某些问题的替代(正确)实现。我试图理解这些差异,以及为什么我会选择一个。在类级别或方法级别泛型之间进行选择

示例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: 给出一些侦听决策的算法的抽象实现。每次做出新决策时,算法都会更新其数据结构,但不会存储决策。由于这是一个抽象类(模板),我们不知道最终用户将如何使用这个类。同样,我们有2种选择:

选项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,注意事项和注意事项的良好参考以支持您的答案。

+0

许多优秀的问题都会根据专家的经验产生一定程度的意见,但对这个问题的回答往往基于意见而非事实,参考或具体专业知识。 –

+0

这些例子都涵盖消费者,考虑供应商的情况(例如'List.get(...)',如果返回'?extends Object'会怎么样)。 –

+0

@RomanC第一个答复已经是一个很好的**事实**答案......并非所有的设计问题都是基于作者的偏好,还是星星的排列... –

回答

3

例1

这两者之间的主要区别在于,你可以用node做什么。

我们只是在第三个选项添加的完整性:

public final Node<? super Number> node; 

记住的缩写PECS

生产者延伸消费者超级

  • 在第二个选项,Node<? extends T> node生产:你只能调用其产生T它的方法。

    T instanceOfT = node.get(); 
    

    ,但你不能调用消费者方法比其他null参数:

    node.accept(null);   // OK. 
    node.accept(instanceOfT); // Compiler error. 
    
  • 在第三个选项,Node<? super T> node消费者:它只能调用方法其中采取一个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的交集(类型是<? extends T><? super T><T>):

    T t = node.get(); // OK. 
    node.accept(t); // OK. 
    

因此,3个选项之间的选择取决于你需要做什么node

  • 如果你需要它来生产T S,选择使用<T><? extends T>;
  • 如果你需要消耗T S,选择使用<T><? super T>;
  • 如果您需要它生产消耗T s,请选择<T>

实施例2

使用第一个如果只想调用的方法的具体参数类型的Decision,例如

AbstractAlgorithm<StringData> foo = new AbstractAlgorithm<>(); 
foo.makeDecision(new Decision<StringData>()); // OK. 
foo.makeDecision(new Decision<IntegerData>()); // Compiler error. 

,如果你希望能够用相同的AbstractAlgorithm,例如调用该方法对于任何类型的使用第二

AbstractAlgorithm foo = new AbstractAlgorithm(); 
foo.makeDecision(new Decision<StringData>()); // OK. 
foo.makeDecision(new Decision<IntegerData>()); // OK. 
+0

hm,那么第一个例子呢? –

+0

@JorisKinable添加。 –