【问题标题】:Why does my self-bound generic type not match the method invocation?为什么我的自绑定泛型类型与方法调用不匹配?
【发布时间】:2020-10-20 21:55:55
【问题描述】:

我正在为如下所示的命令外壳编写代码:

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);
}

我在默认方法中遇到错误提示

The method execute(Command, C) in the type Shell<C> is not applicable for the arguments (Command, RecursiveContext<C>)

我希望这会起作用,因为Shell&lt;C&gt; getShell() 保证能够在其execute 调用中接受C,并且因为this 实际上保证是相同自绑定类型的子类型C,但编译器似乎不同意我的看法。不匹配在哪里,在默认方法中执行强制转换是否安全?如果这不安全,你能提供一个类型不匹配的反例吗?

我也尝试在 shell &lt;D extends C&gt; execute(Command, D) 中引入一个中间类型,但这似乎没有任何改变。

(大多数建议的问题都涉及原始类型的中间步骤,但我认为我没有错过。)

【问题讨论】:

    标签: java generics


    【解决方案1】:

    这里问题的核心是RecursiveContext中的C并不是真正的Self——实现类的类型。 Java 中没有这样的东西,而这个 RecursiveContext&lt;C extends RecursiveContext&lt;C&gt;&gt; 只是解决这个问题的一个技巧,而这就是这个技巧的失败之处。

    假设我有AB。这是有效的:

    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();
        }
    }
    

    我可以这样做:

    new A().execute(new Command());
    

    现在execute 会将thisA 的一个实例)传递给ShellB.execute

    真的会有人像那样实现RecursiveContext吗?我认为不会! 人们理解这种RecursiveContext&lt;C extends RecursiveContext&lt;C&gt;&gt; 模式并“接受提示”。编译器没有(因为自界泛型类型在技术上不是一种语言特性),并说C 仍然可能与this 不是同一类型。

    【讨论】:

    • 那么我能做的最好的防御尝试是在演员/捕捉ClassCastException 并为传递错误工厂生产的Context 的调用代码而兴奋吗?跨度>
    • @chrylis-cautiouslyoptimistic- 在我看来,是的。我的意思是我们都是人类,希望我们知道这种模式意味着什么。如果newContext 没有副作用,我会使用getShell().newContext().getClass() 并将其与this.getClass() 进行比较,以毫无例外地进行检查。
    • 嗯。谢谢。我正在考虑改为将 newContext() 设为实现细节并消除默认实现。
    • 对了,特别感谢反例。这有助于我准确了解故障线的位置并做出设计决定。
    【解决方案2】:

    我认为是因为

    Shell<C> getShell();
    

    不协调 C 到上下文。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-20
      • 1970-01-01
      • 2015-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多