Общий метод сравнения объектов с переменным числом ссылок на методы для сравнения - PullRequest
0 голосов
/ 04 декабря 2018

Мне часто приходится сравнивать с экземплярами определенного типа на равенство, но мне не нужно сравнивать все, а только определенные поля.Обычно я делаю это так:

Comparator<SomeType> c = Comparator.comparing(SomeType::getNumber)
  .thenComparing(SomeType::getType)
  .thenComparing(SomeType::getSite)
  .thenComparing(SomeType::getAddition)
  .thenComparing(SomeType::getImportantFlag);

if (c.compare(old, new) == 0) {
...
}

Поскольку мне приходится это делать очень часто, я задаюсь вопросом, существует ли общий способ сделать это.
Все объекты, которые я должен сравнить, расширяют определенный базовый класс.Есть ли способ написать статический метод, который может сделать все эти сравнения для меня?Я думал о статическом методе, который должен был бы иметь параметры для сравниваемых объектов и параметр vararg для всех ссылок на метод:

public static <T extends BaseType> boolean areFieldsEqual(T left, T right, whatShouldIPutHere... fields) {
}

Но я не знаю, как передать ссылки на метод и какиспользовать их в компараторе в рамках метода.Можно ли это как-то сделать, или я должен попробовать другой подход?

Ответы [ 3 ]

0 голосов
/ 04 декабря 2018

Использование varargs с обобщениями приведет к предупреждению компилятора, например Potential heap pollution via varargs parameter.Как правило, чтобы избежать таких вещей, мы можем предоставить несколько функций, как показано ниже:

  1. С одним полем:

    public static <T extends BaseType> boolean areFieldsEqual(
      T first, T second, Function<? super T, ? extends Comparable> keyExtractor) {
        Comparator<T> comp = Comparator.comparing(keyExtractor);
        return comp.compare(first, second) == 0;
    }
    
  2. С двумя полями:

    public static <T extends BaseType> boolean areFieldsEqual(
          T first, T second, Function<T, ? extends Comparable> firstField,
          Function<T, ? extends Comparable> secondField) {
        Comparator<T> comp = Comparator.comparing(firstField).thenComparing(secondField);
        return comp.compare(first, second) == 0;
    }
    
  3. Использование varargs.Поскольку мы не храним ничего из fields, мы можем использовать @SafeVarargs для этого метода:

    @SafeVarargs
    public static <T extends BaseType> boolean areFieldsEqual(
      T first, T second, Function<T, ? extends Comparable>... fields) {
        if (fields.length < 1) {
          return true;
        }
        Comparator<T> comp = Comparator.comparing(fields[0]);
        for (int i = 1; i < fields.length; i++) {
          comp = comp.thenComparing(fields[i]);
        }
        return comp.compare(first, second) == 0;
    }
    
0 голосов
/ 04 декабря 2018

Вот основная идея, которую вы можете использовать:

List<Function<SomeType, Comparable>> fieldsToTest = new ArrayList<>();
fieldsToTest.add(SomeType::getType);
fieldsToTest.add(SomeType::getSite);
System.out.println(areFieldsEqual(new SomeType(...), new SomeType(...), fieldsToTest));

areFieldsEqual реализация метода:

static <T extends BaseType> boolean areFieldsEqual(T left, T right, 
                                                   List<Function<T, Comparable>> fields) {
    Comparator<T> c = fields.stream().reduce(
        (l, r) -> 0, Comparator::thenComparing, Comparator::thenComparing);
    return c.compare(left, right) == 0;
}

Или, если вы хотите придерживаться varargs параметр:

static <T extends BaseType> boolean areFieldsEqual(T left, T right,
                                                   Function<T, Comparable>... fields) {
    Comparator<T> c = Arrays.stream(fields).reduce(
        (l, r) -> 0, Comparator::thenComparing, Comparator::thenComparing);
    return c.compare(left, right) == 0;
}
0 голосов
/ 04 декабря 2018

Мне удалось придумать что-то, что, кажется, работает:

public static <T extends BaseType> boolean areFieldsEqual(T left, T right, Function<T,? extends Comparable>... fields)
{
    if (fields.length < 1) {
        return true;
    }
    Comparator<T> c = Comparator.comparing(fields[0]);
    for (int i = 1; i < fields.length; i++) {
        c = c.thenComparing (fields[i]);
    }
    return c.compare(left, right) == 0;
}

Тестовые классы:

class BaseType {
    String x;
    int y;
    public BaseType (String x, int y) {
        this.x=x;
        this.y=y;
    }
    String getX () {return x;}
    int getY () { return y;}
}

class SubType extends BaseType {
    String z;

    public SubType (String x, int y,String z) {
        super(x,y);
        this.z=z;
    }
    String getZ () {return z;}
}

Использование:

BaseType one = new BaseType("some",1);
BaseType two = new BaseType("some",2);
SubType three = new SubType("some",1,"else");
SubType four = new SubType("some",2,"else");
System.out.println (areFieldsEqual(one,two,BaseType::getX,BaseType::getY));
System.out.println (areFieldsEqual(three,four,SubType::getZ,BaseType::getX));

Вывод:

false
true
...