Мне часто приходится сравнивать экземпляры определенного типа на равенство, но мне нужно сравнивать не все, а только определенные поля. Я обычно так делаю:
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) {
}
Но я не знаю, как передавать ссылки на методы и как использовать их в компараторе внутри метода. Можно ли это сделать каким-то образом или я должен попробовать другой подход?
Дочерние классы также содержат некоторые параметры, которые необходимо сравнить.




Мне удалось придумать кое-что, что вроде бы работает:
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
Использование varargs с дженериками приведет к предупреждению компилятора, например Potential heap pollution via varargs parameter. Как правило, чтобы избежать таких вещей, мы можем предоставить несколько функций, как показано ниже:
С одним полем:
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;
}
С двумя полями:
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;
}
Используя 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;
}
Отредактировано для передачи другого типа свойств.
Вот основная идея, которую вы можете использовать:
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;
}
Только ли ваш базовый класс содержит все параметры, по которым вы хотите проводить сравнения, или дочерние классы по отдельности могут также содержать некоторые другие параметры, которые следует использовать для проверок сравнения ...?