Reputation: 1321
I have this class:
public abstract class Addressable {
abstract <T extends "this" Addressable> void hardEquals(T t);
}
The method hardEquals(T t) is not bounded as I want. What I want is bound T to be same class of this
. In other words when I extend the class Addressable
, with the concrete class MyAddressable
, I want that the method hardEquals()
has the signature:
void hardEquals(MyAddressable t);
For completeness:
public class MyAddressable extends Addressable {
void hardEquals(MyAddressable t);
}
Upvotes: 10
Views: 910
Reputation: 298409
It’s not possible in general. The problem becomes clear when think a bit longer about your scenario. You have that class Addressable
and its method hardEquals
which demands that its parameter has the type equal to this. Then you have the subclass
public class MyAddressable extends Addressable {
void hardEquals(MyAddressable t);
}
that seems to work as intended. Now imagine a subclass of the subclass:
public class GrandChild extends MyAddressable {
void hardEquals(MyAddressable t);
}
Here, you have a class which must accept MyAddressable
as hardEquals
’ parameter as it is not allowed to override a method by narrowing the parameter types, i.e. not accepting things that the superclass accepted. After all, you can always have a variable of type MyAddressable
that actually refers to an instance of GrandChild
. It would be impossible to restrict the parameters to hardEquals
to match the actual runtime type of the object.
So the desired “parameter type must match this
type” rule conflicts with the “subclass methods must accept all parameters the superclass did” rule.
Note that this is often confused with an actual limitation of the type system, that the same thing doesn’t work for return types. Since return types are allowed to be narrowed for subclasses, the desire to guaranty to return the this
type could be reasonable in some scenarios, e.g.:
class Base {
Base/*actually this type*/ clone() { … }
}
class SubClass extends Base {
SubClass/*actually this type*/ clone() { … }
}
class GrandChild extends SubClass {
GrandChild/*actually this type*/ clone() { … }
}
works, but there is no formal way to specify the guaranty that the this
type is returned so it’s up to the programmer’s discipline to add the correct override to every subclass.
But, as said, for parameter types this doesn’t work in general as you can’t narrow the type of a parameter in a subclass.
Upvotes: 3
Reputation: 65851
If you are looking for a hardEquals
that can only be used with objects with exactly the same type then something like this should work:
public abstract class Addressable<T extends Addressable<T>> {
abstract boolean hardEquals(T t);
}
class X extends Addressable<X> {
@Override
boolean hardEquals(X t) {
return true;
}
}
class Y extends Addressable<Y> {
@Override
boolean hardEquals(Y t) {
return true;
}
}
public void test() {
X x = new X();
if ( x.hardEquals(x)) {
System.out.println("Ok");
}
Y y = new Y();
// Fails!
if ( x.hardEquals(y)) {
System.out.println("Not OK");
}
}
As @Holger rightly points out - this does not help when extending the sub-classes.
Upvotes: 1
Reputation: 48837
I'm not aware of a clean solution for your problem.
You could think about something like this:
public abstract class Addressable<T extends Addressable<T>> {
abstract void hardEquals(T other);
}
public class MyAddressable extends Addressable<MyAddressable> {
@Override
void hardEquals(MyAddressable other) {
// ...
}
}
But this could fail in some cases, e.g.:
public class FakeAddressable extends Addressable<MyAddressable> {
@Override
void hardEquals(MyAddressable aT) {
// ...
}
}
Upvotes: 1
Reputation: 311853
You could borrow a trick from Comparable
, and specify the generic type in the class declaration itself:
public abstract class Addressable<T> {
abstract <T> void hardEquals(T t);
}
This results in slightly clunky class definitions, but it should fulfill your requirement
public class HomeAddress extends Addressable<HomeAddress> {
void hardEquals(HomeAddress t) {
// implementation
}
}
Upvotes: 1