This is a recurring problem when creating self-referencing types. In the base type (or interface), you cannot match this compatibility with T
Of course, you can uncheck this to T if you are sure that all subtypes will fulfill this restriction. But you must complete this unchecked cast when you need a this reference like T
The best solution is to add an abstract method like
public abstract T myself();
Then you can use myself() instead of this when you need self-promotion like T
default Stream<T> getAncestors() { Stream.Builder<T> parentsBuilder = Stream.builder(); for(T node = myself(); node != null; node = node.getParent()) { parentsBuilder.add(parent); } return parentsBuilder.build(); }
Of course, you cannot ensure that subclasses correctly implement myself() as return this; , but at least you can easily check if they are executed at runtime:
assert this == myself();
This comparative comparison is a very cheap operation, and if myself() correctly implemented as invariably returning this , HotSpot can prove in advance that this comparison will always be true and fully validate.
The disadvantage is that each specialization must have this redundant implementation of myself() { return this; } myself() { return this; } , but on the other hand, its completely free from unverified types. An alternative would be to declare abstract abstract in the base class as @SuppressWarnings("unchecked") T myself() { return (T)this; } @SuppressWarnings("unchecked") T myself() { return (T)this; } to limit the unverified operation to one place for the type hierarchy. But then you cannot check if this really has type T ...
source share