CS61B笔记系列 9 Extends, Casting, Higher Order Functions


💡 extends should only be used for **is-a** (hypernymic) relationships!

9.1 Implementation Inheritance: Extends

使用接口的条件 : When a class is a hyponym of an interface, we used implements.

使用继承的条件: one class to be a hyponym of another class, you use extends.

extends 它继承什么:

  • All instance and static variables.
  • All methods.
  • All nested classes.

💡 but members may be private and thus inaccessible!

使用 super 关键字:

public class VengefulSLList<Item> extends SLList<Item> {
private SLList<Item> deletedItems;
    public VengefulSLList() {
       deletedItems = new SLList<Item>();
    }

    @Override
    public Item removeLast() {
            Item oldBack = super.removeLast();
            deletedItems.addLast(oldBack);
            return oldBack;
    }

    public void printLostItems() {
            deletedItems.print();
    }
}

Constructors are not inherited. However, the rules of Java say that all constructors must start with a call to one of the super class’s constructors.

You can explicitly call the constructor with the keyword super (no dot).

If you don’t explicitly call the constructor, Java will automatically do it for you.

为了方便,我们写上 super()

public VengefulSLList() {
   super();
   deletedItems = new SLList<Item>();
} 

当然,重构的构造函数也是一样的调用方法,只是为它加上参数即可

public VengefulSLList(Item x) {
   super(x);
   deletedItems = new SLList<Item>();
}

当然,隐式调用还是调用无参的默认构造函数

PS:略讲了一下 Object 类,不做笔记了

9.2 Encapsulation 封装

9.2.1 Complexity: The Enemy

复杂性是程序员的天敌,部分的解决方法是:

  • Hierarchical abstraction.
    • Create layers of abstraction, with clear abstraction barriers!
  • “Design for change” (D. Parnas)
    • Organize program around objects.
    • Let objects decide how things are done.
    • Hide information others don’t need.

9.2.2 Modules and Encapsulation

Module: A set of methods that work together as a whole to perform some task or set of related tasks.

A module is said to be

encapsulated

if its implementation is completely hidden, and it can be accessed only through a documented interface.

在这里提到了 LAB1 的部分问题,其意思是不要纠结于实现的细节上,实现了接口的功能并且使得接口按照预期即可,也在 ppt 里 “Abstraction Barriers

9.2.3 Implementation Inheritance Breaks Encapsulation 实现继承如何打破封装

public void bark() {
   System.out.println("bark");
}

public void barkMany(int N) {
       for (int i = 0; i < N; i += 1) {
          bark();  
       }
}
public void bark() {
   barkMany(1);
}

public void barkMany(int N) {
       for (int i = 0; i < N; i += 1) {
          System.out.println("bark");  
       }
}

以上两个函数实现的功能是一样的,但是内部却大有不同

这样导致了一个问题的出现:

假设继承 Dog 后的 VerboseDog 没有 override bark(),而且还将 Dog 的函数修改

https://raw.githubusercontent.com/biepin7/CloudForImg/master/20220331155358.png

这时候在调用的时候,会调用 super.bark() ,但是在 super.bark() 中,又调用了 vb.barkMany() ,导致了死循环

9.3 Type Checking and Casting

上来我们整一段代码判断执行结果(有些人忘记了 VengefulSLList )

public static void main(String[] args) {
       VengefulSLList<Integer> vsl = 
          new VengefulSLList<Integer>(9);
       SLList<Integer> sl = vsl;   

        sl.addLast(50);
       sl.removeLast();        

    sl.printLostItems();
    VengefulSLList<Integer> vsl2 = sl;
}
https://raw.githubusercontent.com/biepin7/CloudForImg/master/20220331163409.png
public class VengefulSLList<Item> extends SLList<Item> {
    SLList<Item> deletedItems;

    public VengefulSLList() {
        super();
        deletedItems = new SLList<Item>();
    }

    public VengefulSLList(Item x) {
        deletedItems = new SLList<Item>();
    }

    @Override
    public Item removeLast() {
        Item x = super.removeLast();
        deletedItems.addLast(x);
        return x;
    }

注意这里有两条重要的一直强调的规则

  • If overridden, decide which method to call based on run-time(dynamic type) type of variable.如果被覆盖,请根据 运行时(动态类型) 类型的变量确定要调用的方法。
  • Compiler allows method calls based on compile-time(static type.) type of variable.编译器允许基于编译时(静态类型)类型的变量进行方法调用。

这样到 sl.printLostItems(); VengefulSLList<Integer> vsl2 = sl; 时,会导致一个 Compilation error!

为什么 ?因为 Compiler plays it as safe as possible with type checking.

https://raw.githubusercontent.com/biepin7/CloudForImg/master/20220331164337.png
https://raw.githubusercontent.com/biepin7/CloudForImg/master/20220331164416.png

在我看来这有点是一个向下兼容的问题

这样为了解决这个问题,我们使用 Castingspecifying the compile-time type of any expression. 作用是 Tells compiler to pretend it sees a particular type.

验证一下学到的知识:

public class Bird {
    public void gulgate(Bird b) {
        System.out.println("BiGulBi"); }}

public class Falcon extends Bird {
    public void gulgate(Falcon f) {
        System.out.println("FaGulFa");}}

Bird bird = new Falcon();
Falcon falcon = (Falcon) bird;
bird.gulgate(falcon);
falcon.gulgate(falcon);
BiGulBi
FaGulFa

• Falcon is overloading the gulgate method, not overriding.

9.4 Higher Order Functions in Java

def tenX(x):
    return 10*x

def do_twice(f, x):
    return f(f(x))

print(do_twice(tenX, 2))

9.4.1 Higher Order Functions in Java 7

public interface IntUnaryFunction {
    int apply(int x);
}
public class TenX implements IntUnaryFunction {
    /** Returns ten times the argument. */
    public int apply(int x) {
        return 10 * x;
    }
}

来代替

def tenX(x):
    return 10*x

接下来:

public classHoFDemo {
public static intdo_twice(IntUnaryFunctionf,intx) {
returnf.apply(f.apply(x));
    }

public static voidmain(String[] args) {
IntUnaryFunctiontenX =newTenX();
        System.out.println(do_twice(tenX, 2));
    }
}

10 Subtype Polymorphism vs. HoFs

10 Subtype Polymorphism vs. HoFs

11 Exceptions, Iterators, Object Methods


发表回复