java 检查是否只有2个对象的特定属性相等

wkyowqbh  于 2023-08-02  发布在  Java
关注(0)|答案(6)|浏览(139)

假设我有一个类Person(假设所有属性都可以设置,也可以为null):

public class Person {
  private String firstName;
  private String secondName;
  private Address address;

  public String getFirstName() { return firstName; }
  public String getSecondName() { return secondName; }
  public String getAddress() { return address; }
}

字符串
如果我有两个Person的示例,我想检查它们是否都有相同的firstNamesecondName,我不能简单地调用equals(),因为这也会检查address是否相等(以及任何其他属性)。
我需要写一个这样的函数:

boolean areNamesEqual(Person person1, Person person2) {
  if(person1 == null || person2 == null) {
    return person1 == person2;
  }
  if(person1.getFirstName() != null ? !person1.getFirstName().equals(person2.getFirstName()) : person2.getFirstName() != null) {
    return false;
  }
  if(person1.getSecondName() != null ? !person1.getSecondName().equals(person2.getSecondName()) : person2.getSecondName() != null) {
    return false;
  }
  return true;
}


有没有更清楚的方式来表达这一点?感觉Java为了表达这个简单的想法,似乎跳过了很多圈。我已经开始研究Google Guava,并发现我可以使用Objects.equal()来改善问题:

boolean areNamesEqual(Person person1, Person person2) {
  if(person1 == null || person2 == null) {
    return person1 == person2;
  }
  if(Objects.equal(person1.getFirstName(), person2.getFirstName())) {
    return false;
  }
  if(Objects.equal(person1.getSecondName(), person2.getSecondName())) {
    return false;
  }
  return true;
}


但是我仍然必须检查Person对象本身是否为null,并分别写入getFirstName()和getSecondName()两次。我觉得应该有更好的方式来表达这一点。
像这样的代码是理想的:

(person1, person2).arePropertiesEqual(firstName, secondName)


有了这个,我就不用在任何地方检查null了,我不必提前返回,我也不必多次写入firstNamesecondName
有什么想法吗

5lwkijsr

5lwkijsr1#

使用Java(至少在Java 8和lambdas之前)和Guava,你不会走得太远,因为Java不是一种函数式语言。请参阅Guava Wiki中的Functional Explained中的注意事项部分以获得额外的意见。
实际上,您可以做到这一点,但需要付出更多代码的代价,因此您应该真正评估是否需要它。类似于:

private <T> boolean arePropertiesEqual(T t1, T t2, Function<T, ?>... functions) {
    if (t1 == null || t2 == null) {
        return t1 == t2; // Shortcut
    }
    for (Function<T, ?> function : functions) {
        if (!Objects.equal(function.apply(t1), function.apply(t2))) {
            return false;
        }
    }
    return true;
}

private static class PersonFirstNameFunction 
        implements Function<Person, String> {
    @Override
    public String apply(Person input) {
        return input.getFirstName();
    }
}

private static class PersonLastNameFunction
        implements Function<Person, String> {
    @Override
    public String apply(Person input) {
        return input.getLastName();
    }
}

private void someMethod(Person p1, Person p2) {
    boolean b = arePropertiesEqual(p1, p2, 
            new PersonFirstNameFunction(), new PersonLastNameFunction());
}

字符串
注意:我既没有编译也没有运行上面的代码,由于使用了泛型数组(在varargs中),它可能至少有警告。
在Java 8中,它已经更短了,看起来像这样:

private <T> boolean arePropertiesEqual(T t1, T t2, Function<T, ?>... functions) {
    if (t1 == null || t2 == null) {
        return t1 == t2; // Shortcut
    }
    for (Function<T, ?> function : functions) {
        if (!Objects.equals(function.apply(t1), function.apply(t2))) {
            return false;
        }
    }
    return true;
}

private void someMethod(Person p1, Person p2) {
    boolean b = arePropertiesEqual(p1, p2,
            Person::getFirstName, Person::getLastName);
}


注意这里使用的是java.util.Objectsjava.util.functions.Function,而不是Guava。
现在,这真的比命令式代码“更好”吗?

cuxqih21

cuxqih212#

下面是你的函数的一个稍微短一点的版本:

public boolean areNamesEqual(Person p1, Person p2)  {
   return  (person1 != null  &&  person2 != null  &&
      Objects.equal(p1.getFirstName(), p2.getFirstName())  &&
      Objects.equal(p1.getSecondName(), p2.getSecondName()));
}

字符串

xesrikrc

xesrikrc3#

试试这个:

boolean areNamesEqual(Person person1, Person person2) {
    if (person1 == null || person2 == null) {
        return person1 == person2;
    }
    return person1.getFirstName() == null
        ? person2.getFirstName() == null
        : person1.getFirstName().equals(person2.getFirstName());
}

字符串
否则,您可以使用Objects.equal

Objects.equal(person1.getFirstName(), person2.getFirstName())


所有其他属性都一样。

tpgth1q7

tpgth1q74#

有一个pojomatic API不需要添加太多代码。http://www.pojomatic.org/pojomatic/index.html
之前的例子中,你只需要对pojo使用autoproperty进行注解,对策略为'Equals'的字段使用@property进行注解

import org.pojomatic.Pojomatic;
import org.pojomatic.annotations.AutoProperty;

@AutoProperty
public class Person {
  @Property(policy = PojomaticPolicy.EQUALS)
  private final String firstName;
  @Property(policy = PojomaticPolicy.EQUALS)
  private final String lastName;
  private final int age;

  public Person(String firstName, String lastName, int age) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.age = age;
  }

  public String getFirstName() { return this.firstName; }
  public String getLastName() { return this.lastName; }
  public int getAge() { return this.age; }

  @Override 
  public boolean equals(Object o) {
      return Pojomatic.equals(this, o);
  }

  @Override 
  public int hashCode() {
      return Pojomatic.hashCode(this);
  }

  @Override 
  public String toString() {
     return Pojomatic.toString(this);
  }
}

字符串
现在当你说person1.equals(person 2)时,只考虑名字和最后一次

yizd12fk

yizd12fk5#

您还可以通过反射使用一些更动态的东西,如下所示。

public boolean isObjectPropertiesEqual(Class clazz, Object o0, Object o1, String... properties) {
    for (int i = 0; i < properties.length; i++) {
        // Validate the property value
        String property = properties[i];
        if (property == null || property.isEmpty()) {
            continue;
        }

        // Convert to Java Bean property getter method name
        String getter = "get" + property.substring(0, 1).toUpperCase() + property.substring(1);

        // Check for equality
        try {
            Method method = clazz.getMethod(getter);
            Object value0 = method.invoke(o0);
            Object value1 = method.invoke(o1);
            if (!Objects.equals(value0, value1))
                return false;
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }
    return true;
}

字符串
使用示例:

isObjectPropertiesEqual(Person.class, person1, person2, "firstName", "secondName");

wmomyfyw

wmomyfyw6#

不太想没有更好的方式来表达这一点。“有了这个,我就不用在任何地方检查null了”--好吧,检查null是一个要求,这太糟糕了。我不知道为什么你必须检查null是否等于它自己,或者null是否等于一个Person的名字,或者为什么人们用null而不是名字。这一切对我来说似乎都是无稽之谈,但你可能有一个很好的理由。
您无法避免使用比已经编写的代码更少的代码重复firstName和lastName的访问器。如果你把这个方法放在Person类本身上,你至少应该能够通过直接使用私有字段来避免“get”和“()”。下面是一些代码,并不比你的问题中的代码更好:

public static boolean areBothNullOrBothNonNullAndNamesEqual(Person a, Person b) {
  if (a == null || b == null) {
    return a == b;
  }
  return Objects.equals(a.firstName, b.firstName)
      && Objects.equals(a.lastName, b.lastName);
}

字符串

相关问题