Apologies for the question title, I couldn't put this into words easily.
I've just come across this in some code:
public class MyClass implements Message<MyClass> {...}
I understand what it does but I've never seen a class declared in this way before.
The disadvantage I see is that now MyClass
is a Message and needs to include implemented methods that are unrelated to its primary purpose.
One advantage I see (other than it reduces the number of other classes I would otherwise need to write) is that for things like Comparable
, MyClass
would know how to compare itself to other instance, which in turn would make for more concise code.
Is this good practice? Are there any rules-of-thumb?
Only generic classes can implement generic interfaces. Normal classes can't implement generic interfaces.
It is possible to create a non-generic class that implements a generic interface, provided that the type parameters are provided.
Interfaces can contain methods, properties, events, and indexers. The interface itself does not provide implementations for the members that it declares. The interface merely specifies the members that shall be supplied by classes or structs that implement the interface.
An implemented class can of course have methods NOT declared in its interface . But is bound to implement methods which are declared in the interface unless it is declared abstract .
This is more or less the only way in Java to have an interface with methods that refer to the implementing class itself. So, for example, you might write a binary tree node interface:
interface TreeNode<N extends TreeNode<N>> {
N getLeftChild();
N getRightChild();
void setLeftChild(N node);
void setRightChild(N node);
}
and then you have classes like
class TreeNodeWithInt extends TreeNode<TreeNodeWithInt> {
int value;
TreeNodeWithInt leftChild;
TreeNodeWithInt rightChild;
public TreeNodeWithInt getLeftChild() { return leftChild; }
public void setLeftChild(TreeNodeWithInt newLeft) { leftChild = newLeft; }
...
}
If we didn't have the N
type parameter, you would be forced to write unsafe code like
class TreeNodeWithInt extends TreeNode {
int value;
TreeNodeWithInt leftChild;
TreeNodeWithInt rightChild;
public void setLeftChild(TreeNode node) {
// note the dangerous cast!
leftChild = (TreeNodeWithInt) node;
}
}
The key issue here is that interfaces aren't allowed to refer to the type of the class that's implementing the interface, when they describe the input and return types of methods. So instead, you include a "self" generic type parameter. It's a relatively common idiom in code that makes extensive use of generics.
As you've correctly identified, it's frequently used with Comparable
specifically, because you should only be able to compare objects to other objects of the same type. Indeed, Collections.sort
is specified to take a List<T>
where T extends Comparable<? super T>
, which means that T
is comparable to at least other values of type T
, but possibly others as well.
(Finally, as you might expect -- since it's the only way to achieve this behavior, it's not "good" or "bad" practice. That said, the goal of the technique is to avoid writing methods that compile without warnings but can end up throwing ClassCastException
, which is itself a good practice.)
It is neither "good practice" or "bad practice".
The real question is whether it accurately models what you are trying to achieve. In some cases it will, in others it won't.
And if a particular use of the "meta-pattern" results in unwanted and / or meaningless methods, then you have a strong case that it is not the right solution.
Are there any rules-of-thumb?
If it doesn't work in a particular use-case, don't use it in that use-case :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With