interface A<T extends B</*?*/>> {
method(T param);
}
interface B<U extends A> {
// ...
}
In code snippet above how can it be expressed that method A#method
should only accept parameters that are objects parametrized by type (or its descendants) on which the method is called.
I'd like to achive something like this:
interface Vehicle<T extends SteeringDevice</*?*/> {
default steer(T steeringDevice) {
// ...
}
}
interface SteeringDevice<U extends Vehicle> {
// ...
}
// -----
class Car implements Vehicle<SeeringWheel> {
// ...
}
class SteeringWheel implements SteeringDevice<Car> {
// ...
}
// -----
class Bike implements Vehicle<Handlebars> {
// ...
}
class Handlebars implements SteeringDevice<Bike> {
// ...
}
... where it's possible so safely call new Car().steer(new SteeringWheel())
but not new Car().steer(new Handlebars())
.
I think this does what you want:
interface Vehicle<T extends SteeringDevice<? extends Vehicle<T>>> {
default void steer(T steeringDevice) {}
}
interface SteeringDevice<U extends Vehicle<? extends SteeringDevice<U>>> {
// ...
}
This forces the two compatible implementations to cross-reference each other. Changing the argument to an incompatible type will now trigger a compile error in the paired class.
One potential issue with the above is that it allows for multiple implementations of SteeringDevice<Car>
. You can make the pairing more explicit by adding a self type parameter:
interface Vehicle<U extends Vehicle<U, T>, T extends SteeringDevice<T, U>> {
default void steer(T steeringDevice) {}
}
interface SteeringDevice<T extends SteeringDevice<T, U>, U extends Vehicle<U, T>> {
// ...
}
class Car implements Vehicle<Car, SteeringWheel> {
// ...
}
class SteeringWheel implements SteeringDevice<SteeringWheel, Car> {
// ...
}
It's still possible to create another implementation of SteeringDevice<SteeringWheel, Car>
, because Java doesn't have a true self type, but at least it makes the violation more obvious. It also has the advantage of breaking both classes on an invalid type argument.