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

时间:2014-03-07 13:51:17

标签: java functional-programming guava

假设我有一个类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

有什么想法吗?

5 个答案:

答案 0 :(得分:2)

以下是您的功能的适度缩短版本:

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

答案 1 :(得分:2)

使用Java(至少在Java 8和lambda之前)和Guava,由于Java不是一种函数式语言,因此您无法实现。请参阅Guava Wiki中的Caveats section in 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,而不是番石榴。


现在,这是真的&#34;更好&#34;比命令代码?

答案 2 :(得分:0)

试试这个:

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())

所有其他属性都相同。

答案 3 :(得分:0)

有一个pojomatic API,不需要添加太多代码。 http://www.pojomatic.org/pojomatic/index.html

以前是例子,您需要使用autoproperty为pojo和@property注释政策为'Equals'的字段

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(person2)

时,只考虑名字和最后一次

答案 4 :(得分:-1)

不是,不。没有更好的表达方式。 “有了这个,我没有在任何地方检查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);
}