4.3.子类多态性
\(4.3\)子类多态性
1.多态性的概念
在前面,我们学习了如何运用继承来重复利用已有的代码。继承也可以用于设计具有多态性的通用数据结构与方法。
在\(Java\)中,多态性(\(polymorphism\))代指对象具有不同形式的类型(\(types\))的性质;在面向对象编程中,多态性代指对象具有的不同身份:自己类中的一个实例、主类的一个实例...
考虑下面的问题:我们要实现一个比较函数,用于比较两个对象的大小。我们可以用高阶函数实现:
1
2
3
4def print_larger(x, y, compare, stringify):
if compare(x, y):
return stringify(x)
return stringify(y)
还可以用子类多态性实现: 1
2
3
4def print_larger(x, y):
if x.largerThan(y):
return x.str()
return y.str()
高阶函数给出的是普适的方法,对任何对象都适用。而在子类多态性的方法中,需要对象自己做出决定,执行相应的方法,x.largerThan
方法是由\(x\)与\(y\)来决定的。
2.子类多态性的应用
假设我们想要实现一个普适的max
函数,这个函数接受任意类型的数组,并返回数组中的最大元素。
一个简单的想法是用>
比较: 1
2
3
4
5
6
7
8
9public 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];
}
我们默认>
可以比较任意类型的对象,但是实际上不行。因为>
只能用于确定类型的对象。
于是问题转为实现能比较任意类型对象的>
。我们可以为此创建一个方法接口compareTo
,来确保每个对象都可以调用该方法来比较大小:
1
2
3public interface OurComparable {
public int compareTo(Object o);
}
这样,我们对每个子类逐一实现对应的\(implements\)即可。以\(Dog\)类为例: 1
2
3
4
5
6
7public class Dog implements OurComparable {
public int compareTo(Object o) {
Dog d = (Dog) o;
// notice that compartTo takes in any object,we need to cast o.
return this.size - d.size;
}
}
实现compareTo
后,便可自然地实现max
了:
1
2
3
4
5
6
7
8
9
10public 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
函数。
3.comparable
接口
\(Java\)中已有的comparable
接口可以更好地实现max
,它的形式如下:
1
2
3public interface Comparable<T> {
public int compareTo(T obj);
}
与\(OurComparable\)相比,该接口接受一个通用类型,这使得我们不需要使用类型铸造,同时即使一个类没有继承接口,它也可以调用max
。这样,我们的\(Dog\)类可以如此实现: 1
2
3
4
5
6public class Dog implements Comparable<Dog> {
...
public int compareTo(Dog d) {
return this.size - d.size;
}
}
通过内置(\(built-in\))的接口,我们得以利用已有的库(\(libraries\))和\(Comparable\)接口。
4.comparator
接口
考虑下面的问题:假如我们不想让\(Dog\)以\(size\)排列,该怎么写max
呢?
\(Java\)的内置接口comparator
内部如下:
1
2
3public interface Comparator<T> {
int compare(T o1, T o2);
}
这说明comparator
需要子类实现compare
方法,它和compareTo
的规则相似。我们可以如下实现\(Dog\)的compare
方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import 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);
}
}
//declared it as static,for it doesn't use any other methods
public static Comparator<Dog> getNameComparator() {
return new NameComparator();
}
}
这里的\(Dog\)类通过Comparator
的内部接口实现了继承的层次性:我们可以在\(Dog\)内部定义自己需要的比较方法:NameComparator
、SizeComparator
...
5.接口的功能
\(Java\)中的接口为我们提供了函数回调(\(callback\))的能力。有时,一个函数需要其他还未实现的函数的帮助,如max
需要compareTo
。而回调函数就是辅助函数(\(helper\;function\)),如compareTo
。在\(Java\)中,我们把需要的函数封装在接口内。
A Comparable says, "I want to compare myself to another object". It is imbedded within the object itself, and it defines the natural ordering of a type. A Comparator, on the other hand, is more like a third party machine that compares two objects to each other. Since there's only room for one compareTo method, if we want multiple ways to compare, we must turn to Comparator.