为什么 compareTo 使用比较器抛出 NPE,当用户分开时不这样做
Why compareTo throws NPE using a comparator who doesn't do that when user separatedly
我创建了一个从不抛出 NPE 的比较器。但是,在 compareTo 中使用它时,它会抛出 NPE。为什么?
public class Person implements Comparable<Person> {
public static final Comparator<Person> BIRTHDATE_ASCENDING_NULLS_FIRST = Comparator
.nullsFirst(Comparator.comparing(Person::getBirthDate, Comparator.nullsFirst(Comparator.naturalOrder())));
private String name;
private LocalDate birthDate;
public Person() {
super();
}
public Person(String name, LocalDate birthDate) {
this();
this.name = name;
this.birthDate = birthDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalDate getBirthDate() {
return birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
@Override
public String toString() {
return name + " was born on " + (birthDate == null ? "???" : birthDate);
}
@Override
public int compareTo(Person other) {
// BEGIN ADITIONAL TESTS
if (other == null) {
return 1;
} else if (getBirthDate() == null ^ other.getBirthDate() == null) {
// nulls first
return getBirthDate() == null ? -1 : 1;
} else if (getBirthDate() == null) {
// both are null
return 0;
}
System.out.println(this.toString() + ", " + other.toString());
// END ADITIONAL TESTS
return BIRTHDATE_ASCENDING_NULLS_FIRST.compare(this, other);
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + (birthDate == null ? System.identityHashCode(this) : birthDate.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return Objects.equals(birthDate, ((Person) obj).getBirthDate());
}
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(null);
people.add(new Person("John", null));
people.add(new Person("Mary", LocalDate.now().minusYears(20)));
people.add(new Person("George", LocalDate.now().minusYears(10)));
Collections.sort(people, BIRTHDATE_ASCENDING_NULLS_FIRST);
System.out.println(people);
Collections.sort(people);
System.out.println(people);
Collections.sort(people, BIRTHDATE_ASCENDING_NULLS_FIRST.reversed());
System.out.println(people);
// This one throws NPE
Collections.sort(people);
System.out.println(people);
}
当在 Collections.sort
调用中显式比较器时,排序操作没有像预期的那样使用 compareTo
实现。
当不这样做时,排序操作使用 compareTo
的实现。由于此方法调用完全相同的比较器,为什么我会在这里得到 NPE?我的意思是,为什么比较器在从 compareTo
?
调用时不处理 NPE
其他对 sort
的调用不会抛出 NPE,因为它们调用 Comparator.compare
。该调用可能看起来像这样:
theComparatorYouPassedToSort.compare(item1, item2)
实际示例位于 TimSort.java 第 355 行:
if (c.compare(a[runHi++], a[lo]) < 0) {
但是,不接受 Comparator
(抛出 NPE 的那个)的 sort
重载调用 Person.compareTo
。调用类似于:
item1.compareTo(item2)
一个实际的例子位于 ComparableTimSort.java 第 321 行:
while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)
现在,无论 compareTo
的内部逻辑如何处理 null,如果 item1
为 null,上面的代码将抛出 NPE。如果 item2
,参数为 null,则您的 compareTo
实施仅防止 NPE。它可以处理空参数,但不能改变 Person.compare
的调用方式。如果在空对象上调用它,仍然会抛出 NPE。
我创建了一个从不抛出 NPE 的比较器。但是,在 compareTo 中使用它时,它会抛出 NPE。为什么?
public class Person implements Comparable<Person> {
public static final Comparator<Person> BIRTHDATE_ASCENDING_NULLS_FIRST = Comparator
.nullsFirst(Comparator.comparing(Person::getBirthDate, Comparator.nullsFirst(Comparator.naturalOrder())));
private String name;
private LocalDate birthDate;
public Person() {
super();
}
public Person(String name, LocalDate birthDate) {
this();
this.name = name;
this.birthDate = birthDate;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public LocalDate getBirthDate() {
return birthDate;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
@Override
public String toString() {
return name + " was born on " + (birthDate == null ? "???" : birthDate);
}
@Override
public int compareTo(Person other) {
// BEGIN ADITIONAL TESTS
if (other == null) {
return 1;
} else if (getBirthDate() == null ^ other.getBirthDate() == null) {
// nulls first
return getBirthDate() == null ? -1 : 1;
} else if (getBirthDate() == null) {
// both are null
return 0;
}
System.out.println(this.toString() + ", " + other.toString());
// END ADITIONAL TESTS
return BIRTHDATE_ASCENDING_NULLS_FIRST.compare(this, other);
}
@Override
public int hashCode() {
int result = 1;
result = 31 * result + (birthDate == null ? System.identityHashCode(this) : birthDate.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return Objects.equals(birthDate, ((Person) obj).getBirthDate());
}
public static void main(String[] args) {
List<Person> people = new ArrayList<>();
people.add(null);
people.add(new Person("John", null));
people.add(new Person("Mary", LocalDate.now().minusYears(20)));
people.add(new Person("George", LocalDate.now().minusYears(10)));
Collections.sort(people, BIRTHDATE_ASCENDING_NULLS_FIRST);
System.out.println(people);
Collections.sort(people);
System.out.println(people);
Collections.sort(people, BIRTHDATE_ASCENDING_NULLS_FIRST.reversed());
System.out.println(people);
// This one throws NPE
Collections.sort(people);
System.out.println(people);
}
当在 Collections.sort
调用中显式比较器时,排序操作没有像预期的那样使用 compareTo
实现。
当不这样做时,排序操作使用 compareTo
的实现。由于此方法调用完全相同的比较器,为什么我会在这里得到 NPE?我的意思是,为什么比较器在从 compareTo
?
其他对 sort
的调用不会抛出 NPE,因为它们调用 Comparator.compare
。该调用可能看起来像这样:
theComparatorYouPassedToSort.compare(item1, item2)
实际示例位于 TimSort.java 第 355 行:
if (c.compare(a[runHi++], a[lo]) < 0) {
但是,不接受 Comparator
(抛出 NPE 的那个)的 sort
重载调用 Person.compareTo
。调用类似于:
item1.compareTo(item2)
一个实际的例子位于 ComparableTimSort.java 第 321 行:
while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)
现在,无论 compareTo
的内部逻辑如何处理 null,如果 item1
为 null,上面的代码将抛出 NPE。如果 item2
,参数为 null,则您的 compareTo
实施仅防止 NPE。它可以处理空参数,但不能改变 Person.compare
的调用方式。如果在空对象上调用它,仍然会抛出 NPE。