Thumbnail: java

CS61B_8

by on under Java
7 minute read

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 基本笔记就做完了,也算是搞懂了一些问题,明天也差不多可以开始看新课了,冲。

java