LEC8 Inheritance, Implements

1 方法重载 Overloading:

在Java中,类方法名字可以相同,但参数不同。例如,Math 类可以具有 add(int a, int b) 方法和 add(float a, float b) 方法。 Java编译器足够智能,可以根据传入的参数选择正确的方法。具有相同名称但不同参数的方法被认为被重载。

2 使代码通用:

考虑一个仅使用 AList 作为参数的 largestNumber 方法。缺点是,无论我们采用 AList 还是 SLListlargestNumber的逻辑都是相同的。我们只是对不同类型的列表进行操作。如果我们使用以前的方法覆盖的想法,则会导致Java文件很长,并且包含许多类似的方法。这段代码很难维护。如果我们用一种方法修复错误,则必须手动将此修复方法复制到所有其他方法。

解决上述问题的方法是定义一个代表 AListSLList 的新引用类型。我们将其称为 List。接下来,我们指定一个“is-a”关系:AList是一个 List。我们对SLList做同样的事情。让我们将其形式化为代码。

3 接口 Interface:

我们使用 interface 关键字而非 class 来创建 List。我们写:

public interface List<Item> { ... }

关键思想是接口指定此 List 可以执行的操作,而不是如何做。 由于所有Lists都有一个 get 方法,因此我们将以下方法签名添加到接口类中:

public Item get(int i);

注意,我们没有定义此方法。 我们只是简单地说,只要我们使用List接口,就应该存在此方法。

现在,我们要指定 AList 为一个 List 。 我们将 AList 的类声明更改为:

public AList<Item> implements List<Item> { ... }

我们可以为 SLList 做同样的事情。 现在,回到我们的 largestNumber 方法,而不是为每种类型的列表创建一个方法,我们可以简单地创建一个以 List 为参数的方法。 只要我们的实际对象实现List接口,该方法就可以正常工作!

4 重写 Overriding:

对于在 List 中定义过的 AList 中的每个方法,我们将在方法签名上方添加一个 @Override。 举个例子:

@Override
public Item get(int i) { ... }

这不是必需的,但是是好的风格,因此我们将需要它。 此外,它还允许我们检查错别字。 如果我们键入错误的方法名称,则如果具有 @Override 标记,则编译器将阻止我们的编译。

5 接口继承:

在形式上,我们说子类从超类继承。 接口包含所有方法签名,每个子类必须实现每个单独的签名。 将其视为合同。 另外,关系可以跨越多代。 例如,C可以从B继承,而B可以从A继承。

6 默认方法 Default Methods:

接口可以具有默认方法。 我们通过以下方式定义:

default public void method() { ... }

我们实际上可以在接口内实现这些方法。 请注意,没有实例变量可以使用,但是我们可以自由使用接口中定义的方法,而不必担心实现。 默认方法应适用于实现该接口的任何类型的对象! 子类不必在任何地方重新实现默认方法。 他们可以简单地免费调用它。 但是,我们仍然可以覆盖默认方法,并在子类中重新定义该方法。

7 静态类型与动态类型 Static vs. Dynamic Type:

Java中的每个变量都有一个静态类型。 这是在声明变量时指定的类型,并在编译时进行检查。 每个变量都有一个动态类型。 该类型在实例化变量时指定,并在运行时检查。 举个例子:

Thing a;
a = new Fox();

这里,Thing 是静态类型,Fox 是动态类型,这是可以的因为所有的foxes都是things。我们也可以:

Animal b = a;

This is fine, because all foxes are animals too. We can do:

Fox c = b;

This is fine, because b points to a Fox. Finally, we can do:

a = new Squid()

This is fine, because the static type of a is a Thing, and Squid is a thing.

8 动态方法选择 Dynamic Method Selection:

规则是,如果我们具有静态类型 X 和动态类型 Y,则如果 Y 重写 X 中的方法,则在运行时,我们将在 Y 中使用该方法。 学生经常混淆重写和重载。

9 重载和动态方法选择:

对于重载方法,动态方法选择不起作用。 考虑下面的代码,Fox扩展了Animal。

1  Fox f = new Fox();
2  Animal a = f;
3  define(f);
4  define(a);

假设我们在同一个类中有以下的重载方法:

public static void define(Fox f) { ... }
public static void define(Animal a) { ... }

第3行将执行 define(Fox f),而第4行将执行 define(Animal a)。 动态方法选择仅在我们重写方法时才适用。 这里没有重写,因此动态方法选择不适用。