类在通用接口中实现自身是一种好习惯吗?

时间:2012-07-19 12:43:44

标签: java generics

对问题标题道歉,我无法轻易将其写成文字。

我刚才在一些代码中遇到过这个问题:

public class MyClass implements Message<MyClass> {...}

我理解它的作用,但我以前从未见过以这种方式宣布的类。

我看到的缺点是现在MyClass是一个消息,需要包含与其主要目的无关的已实现方法。

我看到的一个优点(除了减少我需要编写的其他类的数量)是因为像Comparable这样的事情,MyClass会知道如何将自己与其他实例进行比较,这反过来会使更简洁的代码。

这是好习惯吗?有没有经验法则?

4 个答案:

答案 0 :(得分:5)

这或多或少是Java中唯一能够使用引用实现类本身的方法的接口的方法。因此,例如,您可以编写二叉树节点接口:

interface TreeNode<N extends TreeNode<N>> {
  N getLeftChild();
  N getRightChild();
  void setLeftChild(N node);
  void setRightChild(N node);
}

然后你有像

这样的课程
class TreeNodeWithInt extends TreeNode<TreeNodeWithInt> {
  int value;
  TreeNodeWithInt leftChild;
  TreeNodeWithInt rightChild;
  public TreeNodeWithInt getLeftChild() { return leftChild; }
  public void setLeftChild(TreeNodeWithInt newLeft) { leftChild = newLeft; }
  ...
}

如果我们没有N类型参数,您将被迫编写不安全的代码,如

class TreeNodeWithInt extends TreeNode {
  int value;
  TreeNodeWithInt leftChild;
  TreeNodeWithInt rightChild;

  public void setLeftChild(TreeNode node) {
    // note the dangerous cast!
    leftChild = (TreeNodeWithInt) node;
  }
}

这里的关键问题是,当接口描述输入和返回类型的方法时,不允许接口引用实现接口的类的类型。因此,您需要包含“自我”泛型类型参数。这是代码中相对常见的成语,广泛使用泛型。

正如您已经正确识别的那样,它经常与Comparable一起使用,因为应该只能将对象与同类型的其他对象进行比较。实际上,Collections.sort被指定为List<T> T extends Comparable<? super T>,这意味着T可与至少其他T值相比较1}},但也可能是其他人。

(最后,正如你所料 - 因为它是实现这种行为的唯一方法,它不是“好”或“坏”的做法。也就是说,该技术的目标是避免编写没有警告的编译方法但最终可能会抛出ClassCastException,这本身就是一种很好的做法。)

答案 1 :(得分:1)

这既不是“良好做法”,也不是“不良做法”。

真正的问题是它是否准确地模拟了你想要实现的目标。在某些情况下,它会,但在其他情况下则不会。

如果特定使用“元模式”导致不需要的和/或无意义的方法,那么你就有一个强有力的理由认为它不是正确的解决方案。

  

是否有任何经验法则?

如果它在特定用例中不起作用,请不要在该用例中使用它: - )

答案 2 :(得分:0)

虽然我的例子是用C#表达的,但它转换为Java,并且作为执行类的常见任务的有用习惯用语是有意义的,特别是对于域类。也就是说,这既不好也不坏。

class Product :  IEquatable<Product>, ICloneable<Product>, IComparable<Product>

或在Java中

class Product implements IEquatable<Product>, ICloneable<Product>, IComparable<Product>

在这个例子中,我可以定义一个产品,它知道如何检查自己是否与另一个相等,克隆自身(可能是一般的可重用方式),比较自己等等。

我经常使用类似上面的代码生成代码,尽可能在根级别放置泛型,并在叶级别扩展更具体的东西。

答案 3 :(得分:0)

很好的问题,正如Stephen C所提到的,这不是一个好的或坏的做法。事实上,我的描述并不准确。这不是一个实现自身的类的情况,而是一个类,它说它是它想要实现的参数化接口的参数。

public class MyClass implements Message<MyClass> {...}

这实际上意味着Message是一个“参数化”接口,这意味着接口本身接受一个参数。在这种情况下,参数恰好与实现参数化类的类相同。但它可能是别的东西。想象一下:

public class UserDAO implements DAO<User> {...}