4.3是博主在做proj1的时候遇到的一个不熟练的知识点,重点关于多态,包含了Java中高级函数的实现,以及泛型,接口,继承等概念的使用.
多态的引入
假设我们想编写一个 python 程序,用于打印两个对象中较大的对象的字符串表示形式。有两种方法可以做到这一点。
- 显式高阶函数方法
def print_larger(x, y, compare, stringify):
if compare(x, y):
return stringify(x)
return stringify(y)
- 子类多态方法
def print_larger(x, y):
if x.largerThan(y):
return x.str()
return y.str()
使用显式高阶函数方法,我可以打印出2个数中较大的那个,但是使用子类多态方法是对象本身做出选择
实现OurComparable
重点实现了一个优雅的MAX方法
Say we want to write a
max
function which takes in any array - regardless of type - and returns the maximum item in the array.
首先来看个错误的案例:
public static Object max(Object[] items) {
int maxDex = 0;
for (int i = 0; i < items.length; i += 1) {
if (items[i] > items[maxDex]) {
maxDex = i;
}
}
return items[maxDex];
}
public static void main(String[] args) {
Dog[] dogs = {new Dog("Elyse", 3), new Dog("Sture", 9), new Dog("Benjamin", 15)};
Dog maxDog = (Dog) max(dogs);
maxDog.bark();
}
上面代码有一个编译错误,那就是在if (items[i] > items[maxDex])
这个语句的时候,>
不能直接比较两个Object对象.
我们可以创建一个接口来保证任何实现类都包含一个比较方法,我们称之为compareTo
编写接口并指定方法 :
public interface OurComparable {
public int compareTo(Object o);
}
我们可以这样定义判断规则:
- if
this
>o
return -1 - if
this
=o
return 0 - if
this
>o
return 1
接下来,我们就要让Dog
类来实现接口了
public class Dog implements OurComparable {
private String name;
private int size;
public Dog(String n, int s) {
name = n;
size = s;
}
public void bark() {
System.out.println(name + " says: bark");
}
public int compareTo(Object o) {
Dog uddaDog = (Dog) o;
if (this.size < uddaDog.size) {
return -1;
} else if (this.size == uddaDog.size) {
return 0;
}
return 1;
}
}```
现在,我们就可以将`Max`方法推广为可以接受任何`OurComparable`对象的方法了
```Java
public class Max {
public static OurComparable max(OurComparable[] items) {
int maxDex = 0;
for (int i = 0; i < items.length; i += 1) {
int cmp = items[i].compareTo(items[maxDex]);
if (cmp > 0) {
maxDex = i;
}
}
return items[maxDex];
}
}
博主写到这里的时候其实非常纳闷,为什么
max
的类型是OurComparable
呢,这玩意不是一个接口吗?
在上面我们的Dog
类实现了OurComparable
,所以Dog
相当于OurComparable
的子类,这里OurComparable
[]接受的其实是我们传过去的Dog[]
这里提出一个思考,那么没有实现OurComparable
的类,还能使用max
方法吗?
答案是当然不能的,所以我们将要引入Java内建的Comparable
,后面就会讲到
其实可以修改compareTo
的规则来让代码更加简洁:
- Return negative number if
this
< o.
如果this
< o,则返回负数。 - Return 0 if
this
equals o.
如果this
等于 o,则返回 0。 - Return positive number if
this
> o.
如果this
> o,则返回正数。
代码示例:
public int compareTo(Object o) {
Dog uddaDog = (Dog) o;
return this.size - uddaDog.size;
}
Java内建的Comparable
但是这样仍然不完美
- 比如已经存在的类,如
String
,他就没有实现OurComparable
,难道我们还要重写String
?那显然是不现实的,那我们该怎么办? comparaTo
实现中需要进行Dog uddaDog = (Dog) obj;
这种强制类型转换(casting),不好看且不安全
解决方案是直接使用Comparable
采用了泛型,这样就避免了将对象转换为特定的类型
public class Dog implements Comparable<Dog> {
...
public int compareTo(Dog uddaDog) {
return this.size - uddaDog.size;
}
}
Comparator
和自定义比较方法
但是思考一下,假如我们想按照别的规则来比较大小该如何实现呢,比如按照名字首字母顺序?
接下来我们定义一个概念:
自然顺序(Natural order)
用于指代特定类 compareTo
的方法中隐含的顺序
Natural order - used to refer to the ordering implied in the compareTo
method of a particular class.
在上面的例子中,自然顺序就是狗的size
大小
Java的解决办法是使用Comparator
接口
public interface Comparator<T> {
int compare(T o1, T o2);
}
这表明接口 Comparator
要求任何实现类都实现该 compare
方法。(嵌套实现)compare
的规则 就像: compareTo
- Return negative number if o1 < o2.
如果 o1 < o2,则返回负数。 - Return 0 if o1 equals o2.
如果 o1 等于 o2,则返回 0。 - Return positive number if o1 > o2.
如果 o1 > o2,则返回正数。
让我们给Dog
类添加一个NameComparator
!
请注意,我们已将 NameComparator 声明为静态类
privateNameComparator
再public一个getNameComparator
import java.util.Comparator;
public class Dog implements Comparable<Dog> {
...
public int compareTo(Dog uddaDog) {
return this.size - uddaDog.size;
}
private static class NameComparator implements Comparator<Dog> {
public int compare(Dog a, Dog b) {
return a.name.compareTo(b.name);
}
}
public static Comparator<Dog> getNameComparator() {
return new NameComparator();
}
}
注意!NameComparator
中的return a.name.compareTo(b.name);
语句中的compareTo
是String
类的compareTo
,并不是我们在上面Dog
类中重写的
注意是按照字符串首字母在字母表中的顺序进行比较,而不是字符串大小
好了,现在我们再来看一下继承关系图
工作原理: Comparator
是内建于Java的,我们可以在Dog类中定义我们自己的比较方法(NameComparator,SizeComparator等)
总结
总结一下Comparator
需要在我们自己的比较方法中实现compare
方法
比如
private static class NameComparator implements Comparator<Dog> {
public int compare(Dog a, Dog b) {
...
}
}
而Comparable
要求在实现类中实现compareTo
方法
对于Dog
类来说,我们定义这个compareTo
方法是按照size
来比较
这应该就是所谓的特定类的compareTo
方法的自然顺序
这里给出GPT3.5的解释:
在Java中,实现Comparable接口并重写compareTo方法的类通常被称为具有"自然顺序"(natural ordering)或者"内在顺序"(intrinsic ordering)。这是因为compareTo方法定义了该类对象之间的默认比较规则,即这种比较规则是对象本身固有的,而不是外部传入的比较器所定义的。
因此,你在Dog类中重写的compareTo方法可以被称为该类的"自然顺序"或"内在顺序"比较方法,因为它定义了两个Dog对象之间在特定属性(这里是size)上的默认比较规则。
例如
public class Dog implements Comparable<Dog> {
...
public int compareTo(Dog uddaDog) {
return this.size - uddaDog.size;
}
}
但是我再次强调一下,虽然上面已经说过一次
public int compareTo(Dog uddaDog) {
return this.size - uddaDog.size;
}
private static class NameComparator implements Comparator<Dog> {
public int compare(Dog a, Dog b) {
return a.name.compareTo(b.name);
}
}
NameComparator中的compareTo是String类的重写方法,并不是我们刚刚在Dog类中的重写的!这一点不正体现了Java的多态性吗?这就是我对多态的初步认识