对问题标题道歉,我无法轻易将其写成文字。
我刚才在一些代码中遇到过这个问题:
public class MyClass implements Message<MyClass> {...}
我理解它的作用,但我以前从未见过以这种方式宣布的类。
我看到的缺点是现在MyClass
是一个消息,需要包含与其主要目的无关的已实现方法。
我看到的一个优点(除了减少我需要编写的其他类的数量)是因为像Comparable
这样的事情,MyClass
会知道如何将自己与其他实例进行比较,这反过来会使更简洁的代码。
这是好习惯吗?有没有经验法则?
答案 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> {...}