CS61B-25sp-三章继承

0 阅读6分钟

CS61B三章继承

9. Inheritance I: Interface and Implementation Inheritance

9.1 The Problem of Generality

解决泛用性问题,提出Overload重载,即函数名相同,区分参数类型。

9.2 Hypernyms Hyponyms and the Implements Keyword

上位,下级,继承结构,上位类有时也称接口(interface)本质是contract 合约: specifies what a list must be able to do。继承(实现)是承诺 promise: AList is saying "I promise I will have and define all the attributes and behaviors specified in the List61B interface"

image-20250520164524089.png

"is-a"relationship 就是子类is a(kind of)父类。

9.3 Overriding,Interface Inheritance

AList should be able to fit into a List61B box!

子类实现父类方法的过程(接口继承),叫做覆写,覆写方法上面要加上@Override标记。

public static void main(String[] args) {
    List61B<String> someList = new SLList<String>();
    someList.addFirst("elk");
}

9.4 Implementation Inheritance ,default

实现继承,父类可以自建default关键字开头的方法供所有子类使用,子类同样可以通过Override覆写这些方法。这种覆写往往可以提高某种特定子类运行该方法的效率。(个性化)Individuation

9.5 Implementation vs. Interface Inheritance

Interface inheritance (what): Simply tells what the subclasses are able to do.

Implementation inheritance (how): Tells the subclasses how to implement their behavior.

9.6 Abstract Data Types

An Abstract Data Type (ADT) is defined only by its operations, not by its implementation.

we say that ArrayDeque and LinkedListDeque are implementations of the Deque ADT.

img

(cs61b textbook图片,ADT类图)

ADT's allow us to make use of object oriented programming in an efficient and elegant way.

append:

泛型数组,由于Java类型擦除机制,创建数组时,必须实例化为object对象数组,可再通过类型转化变为所需类型。

解决方法:

Box<String>[] boxes = (Box<String>[]) new Box[10]; 

//或者使用Array.newInstance
import java.lang.reflect.Array;

@SuppressWarnings("unchecked")
Box<String>[] boxes = (Box<String>[]) Array.newInstance(Box.class, 10);

10. Inheritance II: Subtype Polymorphism, Comparators, Comparables, Generic Functions

10.1 Polymorphism vs. Function Passing

姑且称之为预制名,Python似乎提供了很多可以直接设定的operator overloading,例如:__gt__,这样,解释器能够自动识别一些约定俗成的类内常用方法。ChatGPT称之为:协议接口实现

Java 没有类似 Python 的 __gt____add__ 等魔术方法!

Java 是一门 强类型、静态绑定、拒绝隐式魔法行为 的语言,因此:

  • Java 不允许操作符重载(Operator Overloading)
  • 除了 + 被用于字符串连接,其它操作符不支持重载
  • Java 更倾向于通过 接口显式方法名 来扩展行为

10.2 Comparables and Comparators

Comparables

我想起了刚才 what&how 的 metaphor 所谓实现继承,即是决定子类如何执行父类定义的方法。

实现继承Comparables,重载compareTo方法

public class Dog implements Comparable<Dog> {
   ...
   @Override
   public int compareTo(Dog uddaDog) {
       if (size > uddaDog.size) {
           return 1;
       }
       if (size < uddaDog.size) {
           return -1;
       }
       return 0;
       
       //在理解上述compareTo规则的情况下,一般写作
       return size - uddaDog.size
       //我之前很迷惑比较器在排序中的行为,实际上,如果分开来看,比较器的行为就是根据预设的规则简单地返回数字,至于排序方法,等到用到时再说吧。
   }
}

主要用于自然顺序的比较

List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog("Grigometh", 200));
dogs.add(new Dog("Pelusa", 5));
dogs.add(new Dog("Clifford", 9000));
Dog maxDog = Collections.max(dogs);

实际上Collections.max() 默认使用的是 “compareTo 返回值为正,表示前者比后者大” 的逻辑。这一点,在comparator中也一样。

Comparators

实现继承Comparators,重载compare方法

public static class NameComparator implements Comparator<Dog> {
   @Override
   public int compare(Dog a, Dog b) {
       //组合接口行为,String本身实现继承了Comparables。
       return a.name.compareTo(b.name);
   }
}

主要用于自定义顺序的比较

Dog a = new Dog("Frank", 1);
Dog b = new Dog("Zeke", 1);
Comparator<Dog> nc = new Dog.NameComparator();
System.out.println(nc.compare(a, b));

比较器也可以作为参数传入一些方法:

List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog("Grigometh", 200));
dogs.add(new Dog("Pelusa", 5));
dogs.add(new Dog("Clifford", 9000));
Dog maxNameDog = Collections.max(dogs, new Dog.NameComparator());

我们也可以进行一点优化,将在max函数传参的比较器对象提前在dog类中设置一个静态成员对象。这样在传参时,我们只需要传递类中的这个变量。

public static final Comparator<Dog> NAME_COMPARATOR = new NameComparator();

Dog maxNameDog = Collections.max(dogs, Dog.NAME_COMPARATOR);

10.3 Writing a Max Function

Generic Functions

泛型函数是指在方法声明中使用了类型参数(type parameters)的函数,允许你在调用方法时指定具体的类型。

public class Maximizer {
   public static <T extends Comparable<T>> T max(T[] items) {
       T maxItem = items[0];
       for (int i = 0; i < items.length; i += 1) {
           int cmp = items[i].compareTo(maxItem);
           if (cmp > 0) {
               maxItem = items[i];
           }
       }
       return maxItem;
   }
}

我太执着于一些错误的过程了,实际上,只要知道对为什么对也不错了。

我们在static后面声明,该泛型函数,要作用于可比较的任意类型,这样我们就不用在调用时初始化RandomPicker函数以指定作用类型,同时规避实例化对象不能调用类静态方法的问题。

12. Inheritance III: Iterators, Object Methods

12.1 Lists and Sets in Java

java.util.List<Integer> L = new java.util.ArrayList<>();

List is an interface we can't instantiate it! We must instantiate one of its implementations.

Sets are a collection of unique elements.

12.2 Exceptions

在实现ArraySet的过程中,如果在set中添加了null,由于contains方法会迭代检查items[i].equals(x)在检查到null时,会触发NullPointerException的问题。虽然也可以通过:

1,人为控制不添加null

2,在contains中遇到null直接跳过

等方法规避,但是我们可以抛出一个异常对象,附加对错误原因的说明,便于检查。

/* Associates the specified value with the specified key in this map.
   Throws an IllegalArgumentException if the key is null. */
public void add(T x) {
    if (x == null) {
        throw new IllegalArgumentException("can't add null");
    }
    if (contains(x)) {
        return;
    }
    items[size] = x;
    size += 1;
}

12.3 Iteration

他说的比我好:

Here we've seen Iterable, the interface that makes a class able to be iterated on, and requires the method iterator(), which returns an Iterator object. And we've seen Iterator, the interface that defines the object with methods to actually do that iteration. You can think of an Iterator as a machine that you put onto an iterable that facilitates the iteration. Any iterable is the object on which the iterator is performing.

可迭代接口中定义了迭代器接口,实现可迭代接口,即可在该类型数据中使用for-each循环,我们也可以只实现迭代器保证迭代功能,而不实现可迭代接口,只是用不了for-each循环:(ChatGPT提供的最短实现)

public Iterator<Integer> getIterator() {
        return new Iterator<>() {
            private IntNode current = sentinel.next;
            @Override public boolean hasNext() {
                return current != null;
            }
            @Override public Integer next() {
                int val = current.item;
                current = current.next;
                return val;
            }
        };
    }

12.4 Object Methods

toString:
String s = dog.toString()
System.out.println(s)

默认的toString方法会打印十六进制虚拟机地址,为了避免过多拷贝提升性能,使用StringBuilder:

public String toString() {
        StringBuilder returnSB = new StringBuilder("{");
        for (int i = 0; i < size - 1; i += 1) {
            returnSB.append(items[i].toString());
            returnSB.append(", ");
        }
        returnSB.append(items[size - 1]);
        returnSB.append("}");
        return returnSB.toString();
    }
equals():
public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (other == null) {
            return false;
        }
        if (other.getClass() != this.getClass()) {
            return false;
        }
        ArraySet<T> o = (ArraySet<T>) other;
        if (o.size() != this.size()) {
            return false;
        }
        for (T item : this) {
            if (!o.contains(item)) {
                return false;
            }
        }
        return true;
    }

Rules for Equals in Java: Overriding a .equals() method may sometimes be trickier than it seems. A couple of rules to adhere to while implementing your .equals() method are as follows:

1.) equals must be an equivalence relation

  • reflexive: x.equals(x) is true
  • symmetric: x.equals(y) if and only if y.equals(x)
  • transitive: x.equals(y) and y.equals(z) implies x.equals(z)

2.) It must take an Object argument, in order to override the original .equals() method

3.) It must be consistent if x.equals(y), then as long as x and y remain unchanged: x must continue to equal y

4.) It is never true for null x.equals(null) must be false

of():
 public static <Glerp> ArraySet<Glerp> of(Glerp... stuff) {
        ArraySet<Glerp> returnSet = new ArraySet<Glerp>();
        for (Glerp x : stuff) {
            returnSet.add(x);
        }
        return returnSet;
    } 

综合练习:

只迭代比较大于传参T的对象:

public class ArraySetGreaterIterator implements Iterator<T> {
    private int pos;
    private T ref;
        
    private Comparator<T> comp;

    public ArraySetGreaterIterator(T ref, Comparator<T> comp) {
        this.ref = ref;
        this.comp = comp;
    }

    @Override
    public boolean hasNext() {
        return pos < size;
    }

    @Override
    public T next() {
        T returnItem = items[pos];
        while (comp.compare(returnItem, ref) <= 0) {
            pos += 1;
            returnItem = items[pos];
        }
        return returnItem;
    }
}

自此,三章继承写完,耗时两天。