0
class County{
    private LocalDate date;
    private String county;
    private String district;
    private String region;
    private Integer cases;
// getters and setters and constructor
}
class District{
    private LocalDate date;
    private String district;
    private String region;
    private Integer cases;
// getters and setters and constructor
}

I have a List<County> countyData and List<District> districtData and I would like to use one method to stream both of them. I have successfully been able to stream them by writing two separate methods, but that is not DRY(don't repeat yourself). I would like to write one method that can perform these streams on a list of either objects.

Here is what worked for me but isn't DRY.

List<LocalDate> labels = countyData.stream().map(c -> c.getDate()).collect(Collectors.toList());
List<Integer> totalCases = countyData.stream().map(c -> c.getTotalCases()).collect(Collectors.toList());

and

List<LocalDate> labels = districtData.stream().map(c -> c.getDate()).collect(Collectors.toList());
List<Integer> totalCases = districtData.stream().map(c -> c.getTotalCases()).collect(Collectors.toList());


Here is my attempt at creating one method that handles both

public <T> void genericMethod(List<T> dataList) {

    Collections.reverse(dataList); 

    List<LocalDate> labels = dataList.stream().map(c -> c.getDate()).collect(Collectors.toList());
    List<Integer> totalCases = dataList.stream().map(c -> c.getTotalCases()).collect(Collectors.toList());

}

But I get the following warning: Cannot infer type argument(s) for <R> map(Function<? super T,? extends R>)

Jac Frall
  • 403
  • 7
  • 15
  • `District extends Country` makes much sense to me. – Aniket Sahrawat Jun 09 '20 at 19:45
  • If county contains`district` `String` why not just have it contain the actual `District` object, both classes are essentially duplicates bar `county` having a `county` field – 123 Jun 09 '20 at 20:12

3 Answers3

2

Just use polymorphism (this is what Object Oriented Programming is for) : make your County and District classes implement a common interface that contains both methods getDate() and getTotalCases()

So your method become :

public void genericMethod(List<YourInterface> dataList)
Fabien
  • 974
  • 9
  • 14
  • Wouldn't it need to be `public void genericMethod(List extends YourInterface> dataList)` – Jac Frall Jun 09 '20 at 22:27
  • Yes you can use a bounded wildcard if you plan to call the generic method with List or List. You won’t be able to add elements to those lists in your method afterthat but it does not seem to be what you were trying to do so it’s ok – Fabien Jun 10 '20 at 07:24
2

A function can be used as a second argument in getList() which can be used for all properties (no changes / no inheritance in County and District):

public static <A,B> List<B> getList(List<A> data, Function<A, B> function) {
    return data.stream().map(c -> function.apply(c)).collect(Collectors.toList());
}

public static void main(String[] args) {
    List<County> counties = new ArrayList<>();
    County county1 = new County();
    county1.setDate(LocalDate.now());
    county1.setCases(1);
    County county2 = new County();
    county2.setDate(LocalDate.now());
    county2.setCases(2);
    County county3 = new County();
    county3.setDate(LocalDate.now());
    county3.setCases(1);

    counties.add(county1);
    counties.add(county2);
    counties.add(county3);

    List<District> districts = new ArrayList<>();
    District district1 = new District();
    district1.setDate(LocalDate.now());
    district1.setCases(11);
    District district2 = new District();
    district2.setDate(LocalDate.now());
    district2.setCases(12);
    District district3 = new District();
    district3.setDate(LocalDate.now());
    district3.setCases(13);

    districts.add(district1);
    districts.add(district2);
    districts.add(district3);

    System.out.println(getList(counties, (c -> c.getDate())));
    System.out.println(getList(districts, (d -> d.getDate())));

    System.out.println(getList(counties, (c -> c.getCases())));
    System.out.println(getList(districts, (d -> d.getCases())));
}

[2020-06-09, 2020-06-09, 2020-06-09]
[2020-06-09, 2020-06-09, 2020-06-09]
[1, 2, 1]
[11, 12, 13]
Serge
  • 194
  • 6
  • A small improvement: instead of writing `(c -> c.getDate())` and `(d -> d.getDate())` you could use method references: `County::getDate` and `District::getDate` respectively – Thomas Kläger Jun 10 '20 at 05:10
0
class Model{
    protected LocalDate date;
    protected String district;
    protected String region;
    protected Integer cases;
    // getters and setters and constructor
}

class County extends Model{
    private String county;

// getters and setters and constructor
}
class District extends Model{

}

public <Model> void genericMethod(List<Model> dataList) {

    Collections.reverse(dataList); 

    List<LocalDate> labels = dataList.stream().map(c -> c.getDate()).collect(Collectors.toList());
    List<Integer> totalCases = dataList.stream().map(c -> c.getTotalCases()).collect(Collectors.toList());

}

This is how we can use inheritance feature in object-oriented programming.

  • I still get the following warning for the code you provided `Cannot infer type argument(s) for map(Function super T,? extends R>)` – Jac Frall Jun 09 '20 at 22:33