4.3.子类多态性

\(4.3\)子类多态性

1.多态性的概念

  在前面,我们学习了如何运用继承来重复利用已有的代码。继承也可以用于设计具有多态性的通用数据结构与方法。

  在\(Java\)中,多态性(\(polymorphism\))代指对象具有不同形式的类型(\(types\))的性质;在面向对象编程中,多态性代指对象具有的不同身份:自己类中的一个实例、主类的一个实例...

  考虑下面的问题:我们要实现一个比较函数,用于比较两个对象的大小。我们可以用高阶函数实现:

1
2
3
4
def print_larger(x, y, compare, stringify):
if compare(x, y):
return stringify(x)
return stringify(y)

  还可以用子类多态性实现:

1
2
3
4
def 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
9
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];
}

  我们默认>可以比较任意类型的对象,但是实际上不行。因为>只能用于确定类型的对象。

  于是问题转为实现能比较任意类型对象的>。我们可以为此创建一个方法接口compareTo,来确保每个对象都可以调用该方法来比较大小:

1
2
3
public interface OurComparable {
public int compareTo(Object o);
}

  这样,我们对每个子类逐一实现对应的\(implements\)即可。以\(Dog\)类为例:

1
2
3
4
5
6
7
public 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
10
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函数。

3.comparable接口

  \(Java\)中已有的comparable接口可以更好地实现max,它的形式如下:

1
2
3
public interface Comparable<T> {
public int compareTo(T obj);
}

  与\(OurComparable\)相比,该接口接受一个通用类型,这使得我们不需要使用类型铸造,同时即使一个类没有继承接口,它也可以调用max。这样,我们的\(Dog\)类可以如此实现:

1
2
3
4
5
6
public 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
3
public 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
19
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);
}
}
//declared it as static,for it doesn't use any other methods

public static Comparator<Dog> getNameComparator() {
return new NameComparator();
}
}

  这里的\(Dog\)类通过Comparator的内部接口实现了继承的层次性:我们可以在\(Dog\)内部定义自己需要的比较方法:NameComparatorSizeComparator...

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.