![Thumbnail: java](https://i.loli.net/2021/10/04/hJaRBNOwYsVZ3M2.png)
CS61B_8
Inheritance Implements
本来这里准备直接放 Array List 的,但是 CS61B 这个地方把 AList 的一些东西和 java 面向对象类的继承之类的东西放在了一起,最后完全体的 AList 会包含一部分这些内容。所以就先打乱下顺序了。
Interface
Interface Inheritance
Specifying the capabilities of a subclass using the implements keyword is known as interface inheritance.
- Interface: The list of all method signatures.
- Inheritance: The subclass “inherits” the interface from a superclass.
- Specifies what the subclass can do, but not how. (也可以声明成 default method)
- Subclasses must override all of these methods! (Will fail to compile otherwise.)
- Such relationships can be multi-generational (多代继承).
对于一个 List 而言,Link List 和 Array List 有很多相同的东西,比如 methods。 可以把这些东西都塞到一个 interface 里面去,只给出声明,没有实现。(想起了 UInterface)
public interface List61B<Item> {
/** 只有声明*/
public void addFirst(Item x);
public void addLast(Item y);
public Item getFirst();
public Item getLast();
public Item removeLast();
public void insert(Item x, int position);
public int size();
}
在写 AList 类的时候可以这样声明成 List61B 的实现,如果把一个类声明成一个接口 (interface) 的实现,那么就必须给出这个接口的所有的函数的实现。
public class AList<Item> implements List61B<Item> {
...
/**
* Why use @Override?
* Main reason: Protects against typos.(防止拼写错误)
* 当标签下的方法并不是重写自父类(比如打错了字),Compile ERROR
* 其次,提醒阅读者这个方法重写自父类
*/
@Override
public void addLast (Item x) {
...
}
}
特大好消息,特大好消息,码农们在 Oracle 大门口跪了三天三夜之后,尊贵的 Oracle 公司决定允许在 interface 里面给出实现 (default implementation) 啦
public interface List61B<Item> {
public void addFirst(Item x);
public void addLast(Item y);
public Item getFirst();
public Item getLast();
public Item removeLast();
public Item get(int i);
public void insert(Item x, int position);
public int size();
default public void print() {
....
}
}
我个人感觉就跟 virtual function 与 pure virtual function 差不多,一开始还挺诧异于 interface 只能给出声明的。。只能说这课上的还挺大喘气。。
显然,你是可以 override (重写) default implementation 的。
public interface SLList<Item> implements {
@Override
public void print() {
for (Node p = sentinel.next; p != null; p = p.next) {
System.out.print(p.item + " ");
}
System.out.println();
}
}
Override VS Overloading
java 中的 override
,和 C++ 差不多,指的是对于父类 (interface) 方法的重写
If a “subclass” has a method with the exact same signature as in the “superclass”, we say the subclass overrides the method.
而 Overloading 指的是重载,即函数名相同但是函数签名不同。
public class Math {
public int abs (int a)
public double abs (double a)
}
。。。并不重要
Static Type vs. Dynamic Type
编译时类型与运行时类型
Every variable in Java has a “compile-time type”, a.k.a. “static type”.
- This is the type specified at declaration. Never changes!
Variables also have a “run-time type”, a.k.a. “dynamic type”.
- This is the type specified at instantiation (实例化) (e.g. when using new).
- Equal to the type of the object being pointed at.
我觉得这一段就是听起来还挺牛逼,但有种听君一席话如听一席话的感觉。。结合下面这个奇坑无比的例子我来给出我的看法
/** Interface */
public interface Animal {
default void greet(Animal a) {
print("hello animal");
}
default void sniff(Animal a) {
print("sniff animal");
}
default void praise(Animal a) {
print("u r cool animal");
}
}
/** implementation class */
public class Dog implements Animal {
// default void greet(Animal a)
@Override
void sniff(Animal a) {
print("dog sniff animal");
}
// default void praise(Animal a)
void praise(Dog a) {
print("u r cool dog");
}
}
public static void main(String[] args) {
Animal a = new Dog();
Dog d = new Dog();
a.greet(d); // "hello animal"
a.sniff(d); // "dog sniff animal"
d.praise(d); // "u r cool dog"
/** guess why */
a.praise(d); // “u r cool animal”
}
上述代码的除了第四个都比较一目了然,但第四个属实给我折腾的人麻了。花了很长一段时间才搞清楚为什么是这个结果,这里给出几点总结
@Override 并不对代码产生任何实质性的影响 (除非你字打错了)
一开始我误认为 class Dog 的 void praise(Dog a); 是 Animal 接口的 override, 然后想偏了就误认为加了 @Override 才算话,事实当然并非如此。
Dog 类的 void praise(); 并不是 override 而是 overloading
其实单把这两个函数拿出来的话还是比较容易看出问题来的
/** Parameter: Animal a */
default void praise(Animal a) {
print("u r cool animal");
}
/** Parameter: Dog a */
void praise(Dog a) {
print("u r cool dog");
}
看出问题了吧,参数列表不同,所以函数签名不同,所以这其实是 overloading
(重载),所以这里压根就没法加 @Override
static types (静态类型) 决定调用哪个函数
到这才是重点,a.praise(d);
为什么这传了个 Dog 进去,调的还是 a 的方法
2 step process of dynamic method selection
- At compile time: We determine the signature S of the method to be called.
- S is decided using ONLY static types.
- At runtime: The dynamic type of the invoking object uses its method with this exact signature S.
- By “invoking object”, we mean the object whose method is invoked.
signature 指的就是函数签名
所以这里可以理解为,在编译时期决定调用函数的函数签名,又因为编译时期只看静态类型,所以这个函数必须是静态类型所拥有的。
那么 Dog 类有符合这样函数签名 void praise(Animal a)
的函数么,有,也必须有,(因为是 interface 的实现)但是因为并没有 override,所以看不见。真实的 Dog 类其实长这样:
public class Dog implements Animal {
default void greet(Animal a) // default method
@Override
void sniff(Animal a) {
print("dog sniff animal");
}
/** 调用的实际是这个函数 */
default void praise(Animal a) // default method
void praise(Dog a) { // overloading
print("u r cool dog");
}
}
到这也算是彻底搞明白了,比较有意思的是这里似乎和 C++ 虚函数表有那么一些不同,对于 C++ 虚函数表我了解甚少,回头补上一篇博客。
Interface vs. Implementation Inheritance
Interface Inheritance (a.k.a. what):
- Allows you to generalize code in a powerful, simple way.
Implementation Inheritance (a.k.a. how):
- Allows code-reuse: Subclasses can rely on superclasses or interfaces.
- Gives another dimension of control to subclass designers: Can decide whether or not to override default implementations.
Important: In both cases, we specify “is-a” relationships, not “has-a”.
- Good: Dog implements Animal, SLList implements List61B.
- Bad: Cat implements Claw, Set implements SLList.
至此 CS61B_8 基本笔记就做完了,也算是搞懂了一些问题,明天也差不多可以开始看新课了,冲。