CS61B 课程笔记(Lecture 10 Subtype Polymorphism vs. HoFs)

1. 多态 (Polymorphism)

多态性是面向对象编程的重要概念之一,指通过一个统一的接口来操作不同类型的实体。Java 中,继承和接口的使用都支持多态。它允许开发人员使用通用代码来处理不同的数据类型和对象,从而实现代码的灵活性和可扩展性。

多态的两种形式
  • 子类型多态:通过继承,一个子类可以被视为其父类的类型来处理。
  • 接口多态:通过实现接口,多个类可以共享同一个接口,从而在不同的上下文中使用通用的方法。

2. Max 函数与接口

目标

设计一个可以通用适用于所有 可比较对象Comparable)的 max 方法。

Java 示例

定义一个 OurComparable 接口,指定 compareTo 方法:

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

compareTo 方法的行为如下:

  • 如果 this < o,返回负数。
  • 如果 this == o,返回 0。
  • 如果 this > o,返回正数。

然后我们实现一个 Dog 类,它实现了 OurComparable 接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 otherDog = (Dog) o;
return this.size - otherDog.size;
}
}
说明
  • Dog 类实现了 compareTo 方法。由于 compareTo 接受任何 Object 类型的参数,因此我们需要将其强制转换为 Dog 类型来进行比较。
  • 在这里,我们根据 size 属性进行比较。
Max 函数的实现

编写一个通用的 max 方法,适用于所有实现了 OurComparable 接口的对象数组:

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 函数的好处
  • 代码复用:不需要在每个类中定义专门的 max 方法,比如不需要 Dog.maxDog(Dog[])
  • 多类型支持:可以对多个类型的对象(只要实现了 OurComparable 接口)进行处理。

3. Comparable 接口

Java 内置的 Comparable<T> 接口允许对象进行“自然排序”,即类自身定义的排序规则。Java 中,Comparable 接口广泛应用于许多库中。

接口定义
1
2
3
public interface Comparable<T> {
public int compareTo(T obj);
}
泛型的好处
  • 通过使用泛型,可以避免将对象强制转换为具体类型(如在 OurComparable 中需要将 Object 转换为 Dog)。
使用 Comparable 的 Dog 类
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Dog implements Comparable<Dog> {
private String name;
private int size;

public Dog(String n, int s) {
name = n;
size = s;
}

public int compareTo(Dog otherDog) {
return this.size - otherDog.size;
}
}

4. Comparator 比较器

虽然 Comparable 定义了类的“自然顺序”,但有时我们需要以其他方式对对象进行排序。这时可以使用 Comparator 接口,它允许我们定义不同的排序规则,而不是使用类中的 compareTo 方法。

Comparator 接口
1
2
3
public interface Comparator<T> {
int compare(T o1, T o2);
}
Comparator 的比较规则
  • 如果 o1 < o2,返回负数。
  • 如果 o1 == o2,返回 0。
  • 如果 o1 > o2,返回正数。
实现 Comparator 的 Dog 类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.Comparator;

public class Dog implements Comparable<Dog> {
private String name;
private int size;

public int compareTo(Dog otherDog) {
return this.size - otherDog.size;
}

// 定义一个内部静态类,用于按名字排序
private static class NameComparator implements Comparator<Dog> {
public int compare(Dog a, Dog b) {
return a.name.compareTo(b.name);
}
}

// 获取 NameComparator 实例
public static Comparator<Dog> getNameComparator() {
return new NameComparator();
}
}
Comparator 的使用
1
Comparator<Dog> nameComparator = Dog.getNameComparator();
总结
  • Comparable 是嵌入到对象本身的,用于定义类型的自然顺序。
  • Comparator 更像是一个外部工具,用于比较两个对象。
  • 由于每个类只能有一个 compareTo 方法,如果需要多个排序方式,就必须使用 Comparator

5. 多态与回调机制

在一些情况下,一个函数需要依赖另一个函数(即回调函数)来完成特定任务。例如,max 函数依赖 compareTo 来比较对象的大小。

回调机制的实现
  • 在某些编程语言中,这通过显式传递函数来实现。
  • 在 Java 中,回调函数通常通过接口来实现。例如,在 Comparable 接口中,compareTo 方法就是 max 函数的回调。
接口的作用

Java 中的接口为实现回调提供了灵活性和扩展性,允许一个函数调用另一个可能还没有实现的函数。这提高了代码的模块化和可维护性。

6. 继承、多态与接口的总结

  • 继承与多态:通过继承,我们可以让子类自动继承父类的方法和属性,同时重写方法以实现多态行为。
  • Comparable:用于对象的自然排序,是嵌入到对象中的比较机制。
  • Comparator:用于自定义比较规则,可以定义多种比较方式。
  • 接口的回调机制:通过接口来实现回调,提高了代码的灵活性,尤其在设计通用方法时,例如 max 函数。