6

为问题标题道歉,我不能轻易地用语言表达。

我刚刚在一些代码中遇到了这个:

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

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

我看到的缺点是 nowMyClass是一个 Message 并且需要包含与其主要目的无关的已实现方法。

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

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

4

4 回答 4

8

这或多或少是在 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>where T extends Comparable<? super T>,这意味着它至少与 type 的其他值T相当,但也可能与其他值相当。T

(最后,正如你所料——因为这是实现这种行为的唯一方法,所以它不是“好”或“坏”的做法。也就是说,该技术的目标是避免编写在没有警告的情况下编译但可以结束的方法up throwing ClassCastException,这本身就是一个好习惯。)

于 2012-07-19T12:53:37.147 回答
1

它既不是“好做法”也不是“坏做法”。

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

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

有什么经验法则吗?

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

于 2012-07-19T12:50:12.123 回答
0

虽然我的示例是用 C# 表达的,但它可以转换为 Java,并且可以作为一个有用的习惯用法,用于为类执行常见任务,尤其是对于域类。也就是说,这既不是好的做法,也不是坏的做法。

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

或在 Java 中

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

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

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

于 2012-07-19T13:02:33.753 回答
0

好问题,正如 Stephen C 所提到的,这既不是好的做法,也不是坏的做法。其实我你的描述也不是很准确。这不是一个类实现自己的情况,而是一个类说它是它要实现的参数化接口的参数。

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

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

public class UserDAO implements DAO<User> {...}
于 2012-07-19T13:02:57.233 回答