【问题标题】:Generic object comparison method with a variable number of method references for comparison具有可变数量的方法引用进行比较的通用对象比较方法
【发布时间】:2018-12-04 11:10:44
【问题描述】:

我经常需要比较某种类型的实例是否相等,但我不需要比较所有内容,只需要比较某些字段。我通常这样做:

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) {
...
}

因为我经常这样做,我想知道是否有一种通用的方法可以做到这一点。
我要比较的所有对象都扩展了某个基类。有没有办法编写一个可以为我做所有这些比较的静态方法?我正在考虑一个静态方法,它必须为要比较的对象提供参数,并为所有方法引用提供一个可变参数:

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

但我不知道如何传递方法引用以及如何在方法内的比较器中使用它们。这可以通过某种方式完成还是我应该尝试不同的方法?

【问题讨论】:

  • 是否只有您的基类包含您想要进行比较的所有参数,或者子类也可能包含一些其他应该用于比较检查的参数...?
  • 子类也包含一些需要比较的参数。

标签: java generics comparator


【解决方案1】:

我设法想出了一些似乎可行的方法:

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

【讨论】:

    【解决方案2】:

    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;
      }
      

    【讨论】:

    • 已编辑以传递不同类型的属性。
    【解决方案3】:

    这是您可以使用的基本思想:

    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;
    }
    

    【讨论】:

      猜你喜欢
      • 2016-02-15
      • 1970-01-01
      • 2022-06-17
      • 1970-01-01
      • 2017-08-30
      • 2016-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多