【CS61B】LEC8 Inheritance, Implements
LEC8 Inheritance, Implements
1 方法重载 Overloading:
在Java中,类方法名字可以相同,但参数不同。例如,Math
类可以具有 add(int a, int b)
方法和 add(float a, float b)
方法。 Java编译器足够智能,可以根据传入的参数选择正确的方法。具有相同名称但不同参数的方法被认为被重载。
2 使代码通用:
考虑一个仅使用 AList
作为参数的 largestNumber
方法。缺点是,无论我们采用 AList
还是 SLList
,largestNumber
的逻辑都是相同的。我们只是对不同类型的列表进行操作。如果我们使用以前的方法覆盖的想法,则会导致Java文件很长,并且包含许多类似的方法。这段代码很难维护。如果我们用一种方法修复错误,则必须手动将此修复方法复制到所有其他方法。
解决上述问题的方法是定义一个代表 AList
和 SLList
的新引用类型。我们将其称为 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)
。 动态方法选择仅在我们重写方法时才适用。 这里没有重写,因此动态方法选择不适用。
- 原文作者:jchen
- 原文链接:http://jchenTech.github.io/post/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%92%8C%E7%AE%97%E6%B3%95/CS61BLEC8/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。