如何在泛型和继承中使用方法?

huangapple go评论82阅读模式
英文:

How to use method with generics and inheritance?

问题

以下是翻译好的内容:

有以下类:

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(); // 包含所有Config的子类型
for (Config conf: configs) {
  Step<? extends Config> step = conf.type().getDeclaredConstructor().newInstance();

  step.setConfig(conf); // 编译器报错
}

错误信息:

"The method setConfig(capture#8-of ? extends Config) in the type
Step<capture#8-of ? extends Config> is not applicable for the
arguments (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

英文:

Having the following classes:

public interface Step&lt;C extends Config&gt; {
  void setConfig(C config);
}

and

public class ValidationStep implements Step&lt;ValidationConf&gt; {
  public void setConfig(ValidationConf conf) {}
  // implementation
}

and

public class ProcessStep implements Step&lt;ProcessConf&gt; {
  public void setConfig(ProcessConf conf) {}
  // implementation
}

and

public interface Config {
  Class&lt;? extends Step&gt; type();
}

and

public class ValidationConf implements Config {
  public Class&lt;? extends Step&gt; type() {
    return ValidationStep.class;
  }
}

and

public class ProcessConf implements Config {
  public Class&lt;? extends Step&gt; type() {
    return ProcessStep.class;
  }
}

so, the application needs to dynamically instantiate Step subclasses objects, set the configuration accordingly and run the step, like this.

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

  step.setConfig(conf); // compiler complains

}

Error message:

> "The method setConfig(capture#8-of ? extends Config) in the type
> Step<capture#8-of ? extends Config> is not applicable for the
> arguments (Config)".

Checking the documentation, looks like Java won´t be friendly in this case:
https://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html

What are the possible solutions to overcome this code restriction step.setConfig(conf);?

EDITED [SOLUTION]

Code can be viewed here: https://github.com/danieldestro/cucumber-salad/tree/generics/src/main/java/my/generics

答案1

得分: 2

因为 Step.setConfig(Config) 是一个“消费者”,解决你遇到的“is not applicable for the arguments (Config)”错误的一种方法是像我在这里演示的使用下界

…
List<? extends Config> configs = loadConfigsFromRepository(); // 包含所有Config的子类型

for (Config conf : configs) {
    Step<? super Config> step = conf.type().getDeclaredConstructor().newInstance();

    step.setConfig(conf); // *set*使Step成为一个“消费者”
}
…

这样你就不需要其他答案中提到的类型转换。

我的*loadConfigsFromRepository()* 实现如下

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

Because Step.setConfig( Config ) is a „consumer“, one way to resolve the „is not applicable for the arguments (Config)“ error you get is to use a lower bound like I demonstrate here

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

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

That way you don't need the cast that the other answer proposes.

My loadConfigsFromRepository( ) is implemented like

static List&lt; ? extends Config &gt; loadConfigsFromRepository(){ 
    
    return of( new ValidationConf( ), new ProcessConf( ) );        
}

答案2

得分: 1

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

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

Get rid of the wildcard. You don't need it.

Step&lt;Config&gt; step = (Step&lt;Config&gt;) conf.type().getDeclaredConstructor().newInstance();

答案3

得分: 0

你的方法并不完全正确。我建议你在确实需要时才使用 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 ProcessStep(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);
}
英文:

Your approach is not fully correct. I reccomend you not to use Reflections until you really need it.

Pay attention, that all these implementation should be hidden inside the package and only Step interface should be public. Config implementation holds all data to create Step class, so just delegate it to this.

package steps

public interface Step&lt;C extends Config&gt; {
  void run(Context context);
}

private final class ValidationStep implements Step&lt;ValidationConf&gt; {
    private final ValidationConf config;
    
    public ValidationStep(ValidationConf config) {
        this.config = config;
    }
}

private class ProcessStep implements Step&lt;ProcessConf&gt; {
    private final ProcessConf config;
    
    public ValidationStep(ProcessConf config) {
        this.config = config;
    }    
}

public interface Config {
    
    Step&lt;? extends Config&gt; createStep();
  
}

private class ValidationConf implements Config {
    
    public Step&lt;ValidationConf&gt; createStep() {
        return new ValidationStep(this);
    }
}

private class ProcessConf implements Config {
    public Step&lt;ValidationConf&gt; createStep() {
        return new ProcessConf(this);
    }
}

package foo

List&lt;Config&gt; configs = loadConfigsFromRepository();

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

huangapple
  • 本文由 发表于 2020年9月17日 02:08:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/63925677.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定