Type inference with mutually recursive generic types
It is possible to define mutually recursive generic types like:
interface Parent<P extends Parent<P, C>, C extends Child<P, C>>
{
Set<C> children();
}
interface Child<P extends Parent<P, C>, C extends Child<P, C>>
{
P parent();
}
Now one can write (for example):
class HumanParent implements Parent<HumanParent, HumanChild>
{
public Set<HumanChild> children() { ... }
}
class HumanChild implements Child<HumanParent, HumanChild>
{
public HumanParent parent() { ... }
}
But now I need to write a method (in some other class) like:
static <P extends Parent<P, C>, C extends Child<P, C>>
C someChildOf(P parent)
{
return parent.children().iterator().next();
}
I can use it with concrete types:
HumanParent humanParent = ...;
HumanChild humanChild = someChildOf(humanParent);
But when I try to use it with the generic wildcard type
Parent<?, ?> parent = ...;
Child<?, ?> child = someChildOf(parent); // doesn't compile
then javac complains:
<P,C>someChildOf(P) [...] cannot be applied to
(Parent<capture of ?,capture of ?>)
Is there any way to get type parameters to bind for recursive types
like Parent<?, ?> and Child<?, ?>?
Incidentally you already get the same error with just one type
parameter:
static <E extends Enum<E>> E f(E e) { ... }
Enum<?> e = null;
f(e); // doesn't compile:
<E>f(E) [...] cannot be applied to (java.lang.Enum<capture of ?>)
Of course in this case one can define f() as
static <E extends Enum<?>> E f(E e) { ... }
which compiles fine, but that's not an option with Parent/Child
because it breaks the Parent<->Child type mapping:
static <P extends Parent<?, ?>, C extends Child<?, ?>>
C someChildOf(P parent)
{
return parent.children().iterator().next(); // doesn't compile
}
incompatible types
found : Child<P,C>
required: C
Variations like
static <P extends Parent<?, C>, C extends Child<P, ?>>
C someChildOf(P parent) // doesn't compile
{
return parent.children().iterator().next();
}
type parameter C is not within its bound
type parameter P is not within its bound
obviously don't work either.
Any suggestions?
-- Niklas Matthies