Home > OS >  How to combine parent comparator with child comparator
How to combine parent comparator with child comparator

Time:09-27

I have a parent class

@Data
public class Parent {

    private String xx;

    private int yy;

}

and two child classes that extends the parent class

@Data
public class ChildA extends Parent {

    private String aa;

}


@Data
public class ChildB extends Parent {

    private String bb;

}

P.S. for semplicity the fields of Parent and ChildA, ChildB are few, but there are many more.

I have a common comparator:

Comparator<Parent> commonComparator = Comparator.comparing(Parent::getYy)
                         .thenComparing(Parent::getXx);

and specific comparator for each child:

Comparator<ChildA> childAComparator = Comparator.comparing(ChildA::getAa);

Comparator<ChildB> childBComparator = Comparator.comparing(ChildB::getBb);

If I want to sort the a List of ChildA, how can I combine the commonComparator with the childAComparator ?

I tried :

commonComparator.thenComparing(childAComparator); 

but I got an issue:

The method thenComparing(Comparator<? super Parent>) in the type Comparator is not applicable for the arguments (Comparator< ChildA >)

CodePudding user response:

I don't think there is a function in the core libraries that will compose these comparators for you. But it is a simple function:

private static <T> Comparator<T> compose(
    Comparator<? super T> before, 
    Comparator<T> after) {

  return (o1, o2) -> {
    int diff = before.compare(o1, o2);
    return diff == 0 ? after.compare(o1, o2) : diff;
  };
}

Then you can use it as

private static final Comparator<ChildA> combined = 
    compose(commonComparator, childAComparator);

Note that because thenComparing() has bounds of ? super T, it can only compose the two comparators when the child comparator is applied first, but in this case, the parent comparator must be applied first.

CodePudding user response:

I don't think there's a better way than casting, even if the common comparator were defined as Comparator<? super Parent>:

Comparator<ChildA> combinedComparator = ((Comparator) commonComparator)
        .thenComparing(childAComparator);

This would compile without errors (and doesn't require casts), but doesn't match your requirements:

childAComparator.thenComparing(commonComparator);

The following works too, but it means your common comparator will only accept ChildA instances (unless casting is performed):

Comparator<ChildA> commonComparator = Comparator.comparing(ChildA::getYy).thenComparing(ChildA::getXx);
commonComparator.thenComparing(childAComparator);
  • Related