8. Inheritance, Implements
约 795 字大约 3 分钟
2025-05-10
计算最长字符串
如果要寻找 SLList 中最长的字符串,可以用以下 Java 程序实现:
public static String longest(SLList<String> list) {
int maxDex = 0;
for (int i = 0; i < list.size(); i += 1) {
String longestString = list.get(maxDex);
String thisString = list.get(i);
if (thisString.length() > longestString.length()) {
maxDex = i;
}
}
return list.get(maxDex);
}
但是在这里有个问题:如果我们要想写一个适用于 AList 的 longest 函数,可能就只能重新写一个新的函数。那么就会出现:
public static String longest(SLList<String> list) {…}
public static String
(AList<String> list) {…}
这样的两个函数叫做重载(overloading)
但是重载也有坏处。在这里,longest 函数在 SLList 和 AList 的操作是完全一样的,或者说,两个重载函数的代码块是可以复用的,完全没必要再造一个新的函数。还有,假如说以后还有一个 Deque 的 longest 函数,那么又要重载一个新的函数,挺麻烦的。
接口
为了解决以上的问题,我们引入了接口。
讲解接口之前要讲“is-a”关系。顾名思义,“is-a”就是某个类是什么的意思。比如狗是一种动物,热狗是一种食物。这样的关系也被称为继承关系。
一旦存在这样的两个“is-a”关系(比如狗是一种动物和猫是一种动物),我们就可以规定一个契约,声明一个动物可以干什么(比如叫、吃饭、跑步),然后我们再在狗类和猫类实现这些规定的方法。
public class AList<Item> implements List61B<Item>{...}
这样就描述了一个 implements 接口关系。
Longest 重载方法也就可以用接口来替代了。ALList 和 SLlist 都具有 longest 方法(契约),都是 List 61 B(“is-a”关系),因此可以实现这样的 interface,然后 ALList 和 SList 来 implements。
覆盖
之前讲过重载,是一个类中同名不同参数方法。覆盖是子类覆盖父类的同名方法。
继承关系中的类型
public static void main(String[] args) {
List61B <String> someList = new SLList<String>();
someList.addFirst("elk");
}
接下来分析这个代码。
List 61 B 接口规定了可以做什么,但现在还需要实现怎么做的方法。
我们可以在 List 61 B 中实现 print 函数,然后在 SLList 覆盖这个 print 函数。
但是在执行上面的代码的时候,则会自动执行 SLList 中的 print 方法。这在 Java 中被称为动态类型选择。代码中 List 61 B 被称为静态类型,SLList 被称为动态类型。
当我们运行一个覆盖方法的时候,Java 会自动选择动态类型的方法来运行。
当在一个类中的两个重载方法:
public static void peek(List61B<String> list) {
System.out.println(list.getLast());
}
public static void peek(SLList<String> list) {
System.out.println(list.getFirst());
}
运行下面代码:
SLList<String> SP = new SLList<String>();
List61B<String> LP = SP;
SP.addLast("elk");
SP.addLast("are");
SP.addLast("cool");
peek(SP);
peek(LP);
第一个 peek 方法会运行 SLList 的版本,第二个会运行 List 61 B 的版本。这是因为重载方法只有参数不同,Java 在检查运行哪个版本的时候会通过静态方法来确定。