使用 Comparable 接口禁止继承类型之间的比较
Using Comparable interface to prohibit comparison between inherited types
我正在阅读 Naftalin 的“Java Generics and Collections”一书。我想检查一下我对他们在第 3 章中提出的观点的理解。
他们解释了 Comparable
接口如何让我们控制可以相互比较的类型。在他们提供的下面的代码示例中,他们说无法将 Apple 与 Orange 进行比较。准确地说,他们是这样说的:
Since Apple implements Comparable<Apple>
, it is clear that you can compare apples with apples, but not with oranges.
但是如您所见,在 main
方法的最后一行,我们能够这样做,因为来自基础 class 的 compareTo
最终被调用.所以我认为解决这个问题的方法是通过将 compareTo
方法移动到每个 Apple 和 Orange class是吗?并将其从基础 Fruit class 中移除?或者有更好的方法吗?
abstract class Fruit {
protected String name;
protected int size;
protected Fruit(String name, int size) {
this.name = checkNotNull(name);
this.size = size;
}
@Override
public boolean equals(Object o) {
if (o instanceof Fruit) {
Fruit that = (Fruit) o;
return this.name.equals(that.name) && this.size == that.size;
}
return false;
}
@Override
public int hashCode() {
return name.hashCode() * 29 + size;
}
protected int compareTo(@Nullable Fruit that) {
if (this.size > that.size) {
return 1;
} else if (this.size < that.size) {
return -1;
} else return 0;
}
}
}
class Apple extends Fruit implements Comparable<Apple> {
public Apple(int size) {
super("Apple", size);
}
@Override
public int compareTo(Apple apple) {
return super.compareTo(apple);
}
}
class Orange extends Fruit implements Comparable<Orange> {
protected Orange(int size) {
super("Orange", size);
}
@Override
public int compareTo(Orange orange) {
return super.compareTo(orange);
}
}
class TestFruit {
public static void main(String[] args) {
Apple apple1 = new Apple(1);
Apple apple2 = new Apple(2);
Orange orange1 = new Orange(1);
Orange orange2 = new Orange(2);
List<Apple> apples = List.of(apple1, apple2);
List<Orange> oranges = List.of(orange1, orange2);
// Both of these work as expected
System.out.println(Collections.max(apples)); // Apple{name=Apple, size=2}
System.out.println(Collections.max(oranges)); // Orange{name=Orange, size=2}
// But now, as expected, when you try to create a mixed list, it will no longer work
List<Fruit> mixed = List.of(apple1, orange2);
// Compile error on the below line
System.out.println(Collections.max(mixed));
// But this also works now, because compareTo from the Fruit class
// is being used here.
System.out.println(apple1.compareTo(orange1)); // -1
}
}
- A class 有方法
compareTo(Fruit)
并不意味着它实现了 Comparable<Fruit>
。只有相反才是正确的。
- 在javaAPI中,需要比较的方法是基于
Comparable
接口,而不是检查class是否有compareTo
方法。
由于Fruit
没有实现Comparable<Fruit>
(实现了就是设计问题),书上说的是对的。你可以看到 Collections.max
cannot be called with List<Fruit>
.
IMO,让我们感到困惑的是 Fruit
.
中的方法名称 compareTo
- 名称与
Comparable
方法不明确,
所以按照惯例,我们应该只在实现 Comparable
. 时命名一个方法 compareTo
- 静态绑定可能调用了错误的方法
// Apple#compareTo is called
System.out.println(apple1.compareTo(apple2));
Fruit fruitWhichIsApple2 = apple2;
// Fruit#compareTo is called
System.out.println(apple1.compareTo(fruitWhichIsApple2));
虽然它被标记为protected
(这避免了其他包class的使用),但如果我们将它重命名为其他名称就更清楚了。
我正在阅读 Naftalin 的“Java Generics and Collections”一书。我想检查一下我对他们在第 3 章中提出的观点的理解。
他们解释了 Comparable
接口如何让我们控制可以相互比较的类型。在他们提供的下面的代码示例中,他们说无法将 Apple 与 Orange 进行比较。准确地说,他们是这样说的:
Since Apple implements
Comparable<Apple>
, it is clear that you can compare apples with apples, but not with oranges.
但是如您所见,在 main
方法的最后一行,我们能够这样做,因为来自基础 class 的 compareTo
最终被调用.所以我认为解决这个问题的方法是通过将 compareTo
方法移动到每个 Apple 和 Orange class是吗?并将其从基础 Fruit class 中移除?或者有更好的方法吗?
abstract class Fruit {
protected String name;
protected int size;
protected Fruit(String name, int size) {
this.name = checkNotNull(name);
this.size = size;
}
@Override
public boolean equals(Object o) {
if (o instanceof Fruit) {
Fruit that = (Fruit) o;
return this.name.equals(that.name) && this.size == that.size;
}
return false;
}
@Override
public int hashCode() {
return name.hashCode() * 29 + size;
}
protected int compareTo(@Nullable Fruit that) {
if (this.size > that.size) {
return 1;
} else if (this.size < that.size) {
return -1;
} else return 0;
}
}
}
class Apple extends Fruit implements Comparable<Apple> {
public Apple(int size) {
super("Apple", size);
}
@Override
public int compareTo(Apple apple) {
return super.compareTo(apple);
}
}
class Orange extends Fruit implements Comparable<Orange> {
protected Orange(int size) {
super("Orange", size);
}
@Override
public int compareTo(Orange orange) {
return super.compareTo(orange);
}
}
class TestFruit {
public static void main(String[] args) {
Apple apple1 = new Apple(1);
Apple apple2 = new Apple(2);
Orange orange1 = new Orange(1);
Orange orange2 = new Orange(2);
List<Apple> apples = List.of(apple1, apple2);
List<Orange> oranges = List.of(orange1, orange2);
// Both of these work as expected
System.out.println(Collections.max(apples)); // Apple{name=Apple, size=2}
System.out.println(Collections.max(oranges)); // Orange{name=Orange, size=2}
// But now, as expected, when you try to create a mixed list, it will no longer work
List<Fruit> mixed = List.of(apple1, orange2);
// Compile error on the below line
System.out.println(Collections.max(mixed));
// But this also works now, because compareTo from the Fruit class
// is being used here.
System.out.println(apple1.compareTo(orange1)); // -1
}
}
- A class 有方法
compareTo(Fruit)
并不意味着它实现了Comparable<Fruit>
。只有相反才是正确的。 - 在javaAPI中,需要比较的方法是基于
Comparable
接口,而不是检查class是否有compareTo
方法。
由于Fruit
没有实现Comparable<Fruit>
(实现了就是设计问题),书上说的是对的。你可以看到 Collections.max
cannot be called with List<Fruit>
.
IMO,让我们感到困惑的是 Fruit
.
中的方法名称 compareTo
- 名称与
Comparable
方法不明确,
所以按照惯例,我们应该只在实现Comparable
. 时命名一个方法 - 静态绑定可能调用了错误的方法
// Apple#compareTo is called System.out.println(apple1.compareTo(apple2)); Fruit fruitWhichIsApple2 = apple2; // Fruit#compareTo is called System.out.println(apple1.compareTo(fruitWhichIsApple2));
compareTo
虽然它被标记为protected
(这避免了其他包class的使用),但如果我们将它重命名为其他名称就更清楚了。