Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can i make this work with java generics

Suppose I have this interfaces and concretes:

public interface MeterValue {}
public class MeterValueA implements MeterValue {}
public class MeterValueB implements MeterValue {}

public interface ReportBuilder<T extends MeterValue> {} 
public class ReportBuilderA implements ReportBuilder<MeterValueA> {}
public class ReportBuilderB implements ReportBuilder<MeterValueB> {}

And I have a global instance:

ReportBuilder<MeterValue> reportBuilder;

I would like to vary the reportBuilder instance as follows:

if (isA())
   reportBuilder = new ReportBuilderA();
else
   reportBuilder = new ReportBuilderB();

reportBuilder.desiredMethod();

Doing this I get "Type Missmatch". How can I get my desired functionality? Or how could I improve my class design to be more flexible?

Edit:

The wildcard ? solved the problem. But also introduced other problems. Say i have this methods in the interface:

public interface ReportBuilder<T extends MeterValue> {
     public String getStuff(List<T> values);
} 

public class ReportBuilderA implements ReportBuilder<MeterValueA> {
     @Override
     public String getStuff(List<MeterValueA> values) { return "A"; }
}

public class ReportBuilderB implements ReportBuilder<MeterValueB> {
     @Override
     public String getStuff(List<MeterValueB> values) { return "B"; }
}

Say I have this instance:

  ReportBuilder<? extends MeterValue> reportBuilder = new ReportBuilderA();

And:

  List<? extends MeterValue> list = new ArrayList<MeterValueA>();
  String s = reportBuilder.getStuff(list);

The call to getStuff(...) will not work since list contain wildcards. One fix would be to modify the ReportBuilder interface method to:

public String getStuff(List<? extends MeterValue> values);

But then I break the concrete ReportBuilders. (list is also a global instance where the content may vary)

Any ideas?

like image 452
Filip Avatar asked May 21 '26 03:05

Filip


2 Answers

I think you should declare the report builder variable as:

ReportBuilder<? extends MeterValue> reportBuilder;

This allows reportBuilder to have any ReportBuilder class, with generic type of MeterValue, or any class implementing the interface.

By the way, if you are not familiar with it already, creating the ReportBuilder instances seems to resemble the factory method pattern, it might be worth reading up on it. (and of course the other patterns too.)

like image 199
ppeterka Avatar answered May 23 '26 16:05

ppeterka


public interface MeterValue {}

public class MeterValueA implements MeterValue {}

public class MeterValueB implements MeterValue {}

public interface ReportBuilder<T extends MeterValue> {
    void desiredMethod();
}

public class ReportBuilderA implements ReportBuilder<MeterValueA> {
    @Override
    public void desiredMethod() {}
}

public class ReportBuilderB implements ReportBuilder<MeterValueB> {
    @Override
    public void desiredMethod() {}
}

void f() {
    ReportBuilder<? extends MeterValue> reportBuilder = null;

    if (Math.random() > 0.5)
        reportBuilder = new ReportBuilderA();
    else
        reportBuilder = new ReportBuilderB();

    reportBuilder.desiredMethod();
}

Where: ReportBuilder<? extends MeterValue> means generic parameter can extend MeterValue interface.

See tutorial to read more explanations about the wildcard declaration.


Note: please review this code in your particular case to avoid Parallel inheritance smell. If to add some new MeterValue you should add ReportBuilder it should mean you have probably this problem.


EDIT:

You cannot pass array with subtypes of MeterValue to ReportBuilderA which requeires at least MeterValueA. It is not type safe technic. Instantiate concrete class after inheritance is more appropriate for C++. In Java it is better do not change restricted type after inheritance.

Probable solution, maybe applicable for your concrete case:

1) Use single interface for MeterValue. All differences move to ReportBuilder inheritance. This means you have the parallel inheritance in your project.

2) Use concrete List<MeterValueA> when call getStuff() method. All shared logic for processing List<MeterValueA> and List<MeterValueB> move to methods with signature: public void sharedLogic(List<? extends MeterValue> list)

like image 34
Taky Avatar answered May 23 '26 18:05

Taky



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!