Given a generic Type Result<T>
with the following partial implementation
public class Result<T> {
/* fields, ctor, etc... */
public Result<T> mergeWith(Result<? extends T> other) {
/* fiddle with this and other (acting as producer) and return */
}
/* other methods */
}
and the use of it in a Visitor...
Interface:
public interface Visitor<T> {
T visitFile(FileContext ctx);
T aggregate(T left, T right);
/* ... */
}
Implementation:
public class SomeVisitor implements Visitor<Result<? extends Node>> {
// this works fine, as I can return subtypes of 'Node'
@Override
public Result<FileNode> visitFile() { }
/* ... */
@Override
public Result<? extends Node> aggregate(Result<? extends Node> left,
Result<? extends Node> right) {
// This line completely blows up.
Result<? extends Node> tempResult = left.mergeWith(right);
// Expected type: 'Option<? extends capture of ? extends Node>'
// Have type: 'Option< ? extends Node>'
}
Callsite of SomeVisitor
:
FileContext fileCtx = someOtherService.acquireFileContext();
SomeVisitor visitor = new SomeVisitor();
Result<FileNode> fileNodeResult = visitor.visitFile(fileCtx);
// process the result
The above example fails with the given type-mismatch error messages.
I have already tried to change the signature of .mergeWith to accept the much narrower Result<T>
instead of a Result<? extends T>
. This leads to an expected type of Result<capture of ? extends Node>
in this example. And breaking it in other places, where <? extends T>
is the correct generic type, since other
is a producer in this case.
The only solution that actually works, is casting down both left
and right
to Result<Node>
, merge them and then return:
@SuppressWarnings("unchecked")
Result<Node> tempLeft = (Result<Node>) left;
@SuppressWarnings("unchecked")
Result<Node> tempRight = (Result<Node>) right;
Result<Node> tempResult = tempLeft.mergeWith(tempRight);
Not only is that ugly and introduces temp variables, it's also not getting prettier when I inline the casts.
I would like to know if that is just the ugly truth about Java generics and the limit thereof or if I am doing something wrong.