Search code examples
javafunctionlambdajava-streamrefactoring

Is there a way to reduce multiple methods to one method with Function<? super T, ?> as method parameter?


I have several methods that are basically all the same except for one method that is called in these methods.

Example:

protected List<DeadlineEntity> getEntityOneDeadlines() {
    return deadLineEntityList
        .stream()
        .filter(m -> getEntityOneDeadlineDbs().stream().anyMatch(p -> m.getDb_4() != null && m.getDb_4().equals(p)))
        .collect(Collectors.toList());
}

protected List<DeadlineEntity> getEntityTwoDeadlines() {
    return deadLineEntityList
        .stream()
        .filter(m -> getEntityTwoDeadlineDbs().stream().anyMatch(p -> m.getDb_5() != null && m.getDb_5().equals(p)))
        .collect(Collectors.toList());
}

So the only difference is the method getDB().

Since I don't want to have this method 10 times, I thought of writing this into a method and then controlling it via the input parameters.

My attempt looks like this:

protected List<DeadLineEntity> getDeadlines(List<Integer> deadLineDbList, Function<<? super T, ?> dbProperty) {
    return deadLineEntityList
        .stream()
        .filter(m -> deadLineDbList.stream().anyMatch(p -> ....))
}

In method anyMatch() I do not get further.

This is how I would want to use it:

List<DeadlineEntity> list1 = getDeadlines(getEntityOneDeadlineDbs(), EntityOne::getDb_4());
List<DeadlineEntity> list2 = getDeadlines(getEntityTwoDeadlineDbs(), EntityTwo::getDb_5());

What do you think? is this a good approach? What would be the further procedure in the getDeadlines() method


Solution

  • Just use Supplier to insert the required instance of the List and Function to substitute the getter from DeadlineEntity to T compliant with the getEntityXXX method generic type of the returned List:

    protected <T> List<DeadlineEntity> getDeadlines(Supplier<List<T>> dbProperty, Function<DeadlineEntity, T> getter) {
        return deadLineEntityList
                .stream()
                .filter(m -> dbProperty.get()
                                       .stream()
                                       .anyMatch(p -> getter.apply(m) != null && 
                                                      getter.apply(m).equals(p)))
                .collect(Collectors.toList());
        }
    
    List<DeadlineEntity> one = getDeadlines(this::getEntityOneDeadlineDbs, DeadlineEntity::getDb_4);
    List<DeadlineEntity> two = getDeadlines(this::getEntityTwoDeadlineDbs, DeadlineEntity::getDb_5);
    

    Edit: To make the code a bit more readable, I'd filter out all p equal to null first and then simplify the lambda expression and switch the parameters of the equals call in the anyMatch method to be null-safe:

    protected <T> List<DeadlineEntity> getDeadlines(Supplier<List<T>> dbProperty, Function<DeadlineEntity, T> getter) {
        return deadLineEntityList
                .stream()
                .filter(m -> dbProperty.get().stream()
                        .filter(Objects::nonNull)
                        .anyMatch(p -> p.equals(getter.apply(m))))
                .collect(Collectors.toList());
    }