danieldestro
danieldestro

Reputation: 379

How to use method with generics and inheritance?

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

Upvotes: 1

Views: 139

Answers (3)

deduper
deduper

Reputation: 1964

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

Upvotes: 2

George
George

Reputation: 3030

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

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

Upvotes: 1

Oleg Cherednik
Oleg Cherednik

Reputation: 18245

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

Upvotes: 0

Related Questions