假设我有一个类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
的两个实例,并且我想检查它们是否具有相同的firstName
和secondName
,我就不能简单地调用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,我不必提前返回,而且我不必多次写firstName
或secondName
。
有什么想法吗?
答案 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.Objects
和java.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);
}