英文:
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<C extends Config> {
  void setConfig(C config);
}
and
public class ValidationStep implements Step<ValidationConf> {
  public void setConfig(ValidationConf conf) {}
  // implementation
}
and
public class ProcessStep implements Step<ProcessConf> {
  public void setConfig(ProcessConf conf) {}
  // implementation
}
and
public interface Config {
  Class<? extends Step> type();
}
and
public class ValidationConf implements Config {
  public Class<? extends Step> type() {
    return ValidationStep.class;
  }
}
and
public class ProcessConf implements Config {
  public Class<? extends Step> 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<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
}
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< ? 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“
}
…
That way you don't need the cast that the other answer proposes.
My loadConfigsFromRepository( ) is implemented like…
static List< ? extends Config > 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<Config> step = (Step<Config>) conf.type().getDeclaredConstructor().newInstance();
答案3
得分: 0
你的方法并不完全正确。我建议你在确实需要时才使用 Reflections。
请注意,所有这些实现应该隐藏在包内,只有 Step 接口应该是 public。Config 实现保存了创建 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<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);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论