问题
I'm writing code for a command shell that looks like this:
interface Context<C extends Context<C>> {}
interface RecursiveContext<C extends RecursiveContext<C>> extends Context<C> {
Shell<C> getShell();
default Result execute(Command cmd) { return getShell().execute(cmd, this); }
}
interface Shell<C extends Context<C>> {
C newContext();
Result execute(Command cmd, C context);
}
I'm getting an error in the default method saying
The method execute(Command, C) in the type Shell<C> is not applicable for the arguments (Command, RecursiveContext<C>)
I expect this to work, since the Shell<C> getShell()
is guaranteed to be able to accept C
in its execute
call and because this
is in fact guaranteed to be a subtype of the same self-bound type C
, but the compiler does not seem to agree with me. Where's the mismatch, and is it safe to perform a cast in the default method? If this is not safe, can you provide a counterexample of type mismatch?
I've also tried introducing an intermediate type in the shell <D extends C> execute(Command, D)
, but that doesn't seem to change anything.
(Most of the suggested questions involve a raw-type intermediate step, but I don't think that I've missed that.)
回答1:
The core of the problem here is that C
in RecursiveContext
is not really Self
- the type of the implementing class. There is no such thing in Java, and this RecursiveContext<C extends RecursiveContext<C>>
is merely a trick to workaround that, and this is where that trick breaks.
Let's say I have A
and B
. This is valid:
class A implements RecursiveContext<B> {
@Override
public Shell<B> getShell() {
return new ShellB();
}
}
class B implements RecursiveContext<B> {
@Override
public Shell<B> getShell() {
return null;
}
}
class ShellB implements Shell<B>{
@Override
public B newContext() {
return new B();
}
@Override
public Result execute(Command cmd, B context) {
return new Result();
}
}
And I can do this:
new A().execute(new Command());
Now execute
would pass this
, which is an instance of A
, into ShellB.execute
!
Would anyone actually implement RecursiveContext
like that? I would think not! People understand this RecursiveContext<C extends RecursiveContext<C>>
pattern and "takes the hint". The compiler doesn't (because self-bounded generic types is not technically a language feature), and says C
still might not be the same type as this
.
回答2:
I think its because
Shell<C> getShell();
does not reconcile C to Context.
来源:https://stackoverflow.com/questions/64453876/why-does-my-self-bound-generic-type-not-match-the-method-invocation