How to use method with generics and inheritance?

谁都会走 提交于 2021-02-04 08:21:20

问题


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:


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:


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

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



回答3:


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


来源:https://stackoverflow.com/questions/63925677/how-to-use-method-with-generics-and-inheritance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!