【问题标题】:How to use method with generics and inheritance?如何使用具有泛型和继承的方法?
【发布时间】:2021-01-03 14:49:21
【问题描述】:

有以下类:

public interface Step<C extends Config> {
  void setConfig(C config);
}

public class ValidationStep implements Step<ValidationConf> {
  public void setConfig(ValidationConf conf) {}
  // implementation
}

public class ProcessStep implements Step<ProcessConf> {
  public void setConfig(ProcessConf conf) {}
  // implementation
}

public interface Config {
  Class<? extends Step> type();
}

public class ValidationConf implements Config {
  public Class<? extends Step> type() {
    return ValidationStep.class;
  }
}

public class ProcessConf implements Config {
  public Class<? extends Step> type() {
    return ProcessStep.class;
  }
}

因此,应用程序需要动态实例化 Step 子类对象,相应地设置配置并运行该步骤,就像这样。

List<Config> configs = loadConfigsFromRepository(); // contain all subtypes of Config
for (Config conf: configs) {
  Step<? extends Config> step = conf.type().getDeclaredConstructor().newInstance();

  step.setConfig(conf); // compiler complains

}

错误信息:

"类型中的方法setConfig(capture#8-of ? extends Config) 步骤 不适用于 参数(配置)”。

检查文档,在这种情况下,Java 似乎不友好: https://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html

有哪些可能的解决方案来克服此代码限制step.setConfig(conf);

已编辑 [解决方案]

代码可以在这里查看:https://github.com/danieldestro/cucumber-salad/tree/generics/src/main/java/my/generics

【问题讨论】:

  • “编译器抱怨”到底是什么意思?错误信息是什么?
  • "Step 类型中的方法 setConfig(capture#8-of ? extends Config) 不适用于参数 (Config)"
  • Step.run(Context ctx) 中的 Context 是什么?它神奇地再次出现在您的 for 循环中:“step.run(context)”。它是如何到达那里的?你没有说清楚。
  • 另外,您定义了一个通用类:„Step&lt; C extends Config &gt;“。但随后在 Config 中指定使用它的方法,以使用原始类型:“Class&lt;? extends Step&gt; type();”。这是为什么?与问题无关?
  • ...这与这里的问题无关...“ -​​ 在这种情况下,您是否考虑将其从问题中删除?

标签: java generics reflection


【解决方案1】:

因为 Step.setConfig( Config ) 是“消费者”,解决“is not applicable for the arguments (Config)”错误的一种方法是@ 987654321@…

…  
List< ? extends Config > configs = loadConfigsFromRepository( ); // contain all subtypes of Config
  
for ( Config conf: configs ) {
    Step< ? super Config > step = conf.type( ).getDeclaredConstructor( ).newInstance( );

      step.setConfig( conf ); // *set* makes Step a „consumer“
}
…

这样你就不需要其他答案提出的演员表了。

我的loadConfigsFromRepository( ) is implemented like...

static List< ? extends Config > loadConfigsFromRepository(){ 
    
    return of( new ValidationConf( ), new ProcessConf( ) );        
}

【讨论】:

  • 如果我这样做,我会得到:“类型不匹配:无法从 capture#8-of 转换?将 Step> 扩展为 Step”。而且我不知道如何将interface Step&lt;C extends Config&gt; 更改为使用super,因为我使用C 来定义setConfig 方法参数。
  • ...我得到:“类型不匹配:无法从 capture#8-of 转换?将 Step> 扩展为 Step”...“——你从哪里/怎么得到的? The code I linked to 不会产生任何错误。 — „如何将 interface Step&lt;C extends Config&gt; 更改为使用 super“- 查看我的答案中的代码和我链接到的代码。我没有建议你interface Step&lt;C extends Config&gt; 改为使用super。我建议您更改的唯一内容是 for 循环中的“Step&lt; ? super Config &gt; step”。您在原始 Q 中标记为“编译器抱怨”的内容。
  • 是的。我试过了,它有效。谢谢! github.com/danieldestro/cucumber-salad/tree/generics/src/main/…
  • 很棒的丹尼尔德斯特罗!我很高兴能帮助你。 @Nikolas to a question more recent than yours 的最新回答刚刚被接受和投票。该答案与我几个小时前向您提出的答案基本相同。您同意或不同意我的回答比这里对your 问题的其他两个回答更可接受 答案?如果您不同意,请告诉我:我的答案可以通过哪些方式改进,以便您将其标记为已接受?为什么这三个都不为你所接受? TIA。
  • 嗨@deduper - 我会说这两种解决方案都令人满意并且工作正常。我同意您的解决方案对我来说可能看起来更优雅,避免强制转换,但两者都只是语法糖。
【解决方案2】:

去掉通配符。你不需要它。

Step<Config> step = (Step<Config>) conf.type().getDeclaredConstructor().newInstance();

【讨论】:

  • 我收到一个错误:“类型不匹配:无法从 capture#7-of 转换?将 Step> 扩展为 Step”。
  • 我明白了。这是因为您获得了从type()Config 的粒度类型。此时,您可以直接施放它。我已经更新了我的答案。
  • 啊好吧!现在有了演员表,我只收到一条警告消息:“类型安全:从 capture#7-of 未经检查的演员表?将 Step> 扩展到 Step”。让我运行并检查它是否有效。
  • 从给定的情况来看,它是什么类型的Config都没有关系,所以扔掉Config是可以的。
  • 同意还是不同意 George,如果完全可能,最好避免显式转换?在我看来,应用 PECS 原则是解决@danieldestro 错误的更好(即更安全)的解决方案。这样说公平吗?
【解决方案3】:

您的方法并不完全正确。我建议您在真正需要之前不要使用Reflections

注意,所有这些实现都应该隐藏在包中,只有Step接口应该是publicConfig 实现保存所有数据以创建 Step 类,因此只需将其委托给它。

package steps

public interface Step<C extends Config> {
  void run(Context context);
}

private final class ValidationStep implements Step<ValidationConf> {
    private final ValidationConf config;
    
    public ValidationStep(ValidationConf config) {
        this.config = config;
    }
}

private class ProcessStep implements Step<ProcessConf> {
    private final ProcessConf config;
    
    public ValidationStep(ProcessConf config) {
        this.config = config;
    }    
}

public interface Config {
    
    Step<? extends Config> createStep();
  
}

private class ValidationConf implements Config {
    
    public Step<ValidationConf> createStep() {
        return new ValidationStep(this);
    }
}

private class ProcessConf implements Config {
    public Step<ValidationConf> createStep() {
        return new ProcessConf(this);
    }
}

package foo

List<Config> configs = loadConfigsFromRepository();

for (Config config : loadConfigsFromRepository()) {
  config.createStep().run(context);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-25
    • 1970-01-01
    相关资源
    最近更新 更多