(注:本篇文章有点难了解,所以我间接大部分翻译原文了......题目:Subtype Polymorphism vs. Explicit Higher Order Functions)

1.热身:Dynamic method selection

假如咱们有两个类,Dog和ShowDog,其中showDog implement Dog,且二者都有bark()办法,showDog Override bark()办法

总结一下"is-a"关系,咱们能够失去:

  • 每条showDog都是Dog
  • 每条Dog都是Object
    • Java中所有类型均是Object的子类型

当初,考虑一下以下代码:

Object o2 = new ShowDog("Mortimer","Corgi",25,512.2);ShowDog sdx = ((ShowDog)o2);sdx.bark();Dog dx = ((Dog)o2);dx.bark();((Dog)o2).bark();Object o3 = (Dog) o2;o3.bark();

对于每一行赋值,思考是否会引起编译谬误
每一次对bark()的调用,思考是调用的ShowDog.bark()呢?还是Dog.bark()?还是语法错误

温习一下规定:

  1. 编译器容许内存盒寄存任何子类型的变量
  2. 编译器查看某办法是否能够调用取决于变量的动态类型
  3. Dynamic method selection只对子类Overridden 非静态方法管用,且产生在运行时(runtime),此时对办法的调用取决于变量的动静类型
  4. casting(强制类型转换)并非长时间无效,只霎时作用于应用强制类型转换的某一特定的表达式(相当于在该行代码无效,下一行即生效,变量还原成原来的类型),相当于哄骗编译器:"置信我,该变量就是这种类型",从而绕过编译器的类型查看,实质上强制类型转换并不会扭转变量所指向的实在类型

hiding

hiding是一种bad style,例如:

  • 子类中的变量与父类中的变量同名
  • 子类中有静态方法与父类中的静态方法申明统一(包含参数名)
    • 对于静态方法,咱们不叫Override,称为hiding

以上称为hiding,josh并不打算在61B中传授,因为他认为这是一种语法错误,if you interested it,see this link


2.子类多态(Subtype Polymorphism)

多态:为不同类型的实体提供繁多接口
在Java中,多态性是指对象能够有多种形式或类型。在面向对象编程中,多态性波及到一个对象如何被视为其本身类的实例、其超类的实例、其超类的超类的实例,等等。


3.DIY Comparison

假如咱们当初须要写一个程序,其性能是打印出两个Object中较大的一个
在python中,
1.应用高阶函数显式调用的办法写:

def print_larger(x, y, compare, stringify):    if compare(x, y):        return stringify(x)    return stringify(y)

有时,compare()也叫做回调(callback)
2.应用子类型多态性的办法写:

def print_larger(x, y):   if x.largerThan(y):       return x.str()   return y.str()

应用高阶函数显式调用的办法,你能以一种常见的形式打印出后果,与此相反,在子类型多态性的办法中,对象对largeFunction()的调用取决于x和y实际上是什么

假如咱们想写一个max(),能够返回任何类型的数组中的最大值元素

对于红色方框内的代码,哪一行会编译谬误?
显然是

items[i] > items[maxDex]

因为Java中,Object不能应用 > 进行互相比拟,除此之外,Java中不反对运算符重载,这也意味着Java不能像C++与python重载 >
一种native的解决办法是,在Dog class外面定义max()办法:

public static Dog maxDog(Dog[] dogs) {       if (dogs == null || dogs.length == 0) {       return null; }       Dog maxDog = dogs[0];       for (Dog d : dogs) {       if (d.size > maxDog.size) {            maxDog = d;            }}    return maxDog;}

而后就能够调用:

Dog[] dogs = new Dog[]{d1, d2, d3};Dog largest = Dog.maxDog(dogs);

然而这样做的害处是,如果以后Object不是Dog呢?是Cat , Fish , or Lion?咱们岂不是要为每一个Animal class写一个max()吗?
咱们心愿可能发明一个实用于个别类型的max(),能够比拟任何Animals,因而,Solution is

  • 发明一个interface,并在其中申明一个comparison method
  • 发明Dog class ,implements interface

public interface OurComparable {    public int compareTo(Object o);//参数Obejct 也能改成 OurComparable,无区别}

对于compareTo()函数,定义其返回值为:

  • if 以后对象this < 对象o, 返回正数
  • if 二者相等,返回0
  • if 以后对象this > 对象o, 返回负数
    咱们应用size作为比拟的基准
public class Dog implements OurComparable {    private String name;    private int size;    public Dog(String n, int s) {        name = n;        size = s;    }    public void bark() {        System.out.println(name + " says: bark");    }    public int compareTo(Object o) {    Dog uddaDog = (Dog) o;    return this.size - uddaDog.size;}}

请留神,因为compareTo(Object o) 的传参是任意的Object o,所以咱们须要将Object类型的 o 强制类型转换为 Dog 类型(应用uddaDog寄存o),以保障能够应用 size 变量进行比拟,否则Object class 中没有 size
实现以上工作之后,咱们就能够一般化max()办法了,将实用于任何类型的Animals,而不再繁多对Dog无效:

public class Maximizer {  public static OurComparable max(OurComparable[] items) {    int maxDex = 0;                              //OurComparable也能够改成Object    for (int i = 0; i < items.length; i += 1) {        int cmp = items[i].compareTo(items[maxDex]);        if (cmp > 0) {            maxDex = i;        }    }    return items[maxDex];}}

对Dog进行max():

Dog[] dogs = new Dog[]{d1, d2, d3};Dog largest = (Dog) Maximizer.max(dogs);

此外,如果咱们想比拟Cat class,就发明一个Cat class去implement ourComparable interface,因而,此时的max()是对所有Animals实用


4.Interfaces Quiz


Q1:如果咱们漠视 Dog 类外面的 compareTo() ,哪个文件会编译谬误?

A: Dog.java 会编译谬误,因为 Dog 申明了 implements OurComparable 接口,就示意Dog class 承诺会实现 OurComparable 所申明的所有 method(),而当 Dog class 中并未蕴含这些办法时,便会报错
而且 DogLauncher.java 也会报错,因为 Dog.java 的编译谬误导致 javac 不能生成 Dog.class 文件,那么在 DogLaucher.java 中应用 Dog.则会报错

Q2:如果咱们漠视 Dog class header 中的 implements OurComparable,下列哪个文件会报错?

A: DogLauncher.java 会报错,因为当 Dog class 并未申明 implements 时, Dog class 并不属于 OurComparable 的子类,那么 OurComparable 的内存盒并不能包容 Dog 类型,因而当试图向 Maximizer.max() 传入 Dog 类型的 dogs 数组作为参数时,会报错


5.Comparables接口

咱们实现了 OurComparable 接口,但其仍有许多不完满之处,例如:

  • 每次都要在 Object 之间进行强制类型转换:
Dog uddaDog = (Dog) o;
Dog[] dogs = new Dog[]{d1, d2, d3};Dog largest = (Dog) Maximizer.max(dogs);
  • No existing classes implement OurComparable (e.g. String, etc.)
  • No existing classes use OurComparable (e.g. no built-in max function that uses OurComparable)

Java有一个内置的接口更加欠缺与弱小,其与咱们实现的 OurComparable 类似,且反对泛型

应用:只需将 T 替换成 Dog 即可


6. Comparator

考虑一下Java如何实现函数回调(callback),在python中,应用高阶函数显示回调,compare作为参数传入

def print_larger(x, y, compare, stringify):    if compare(x, y):        return stringify(x)    return stringify(y)

如何批改子类型多态的形式实现回调呢?

def print_larger(T x, T y):   if x.largerThan(y):       return x.str()   return y.str()

退出一个参数 comparator<T> c

def print_larger(T x, T y, comparator<T> c):   if c.compare(x, y):       return x.str()   return y.str()

也就是咱们要介绍的Java另一个内置的接口 Comparator ,上文中咱们比拟的是两条狗的 size 如果咱们按字母的字典序对它们的名字作比拟呢?就能够用到 Comparator
因为 Comparator 是一个 Object,咱们应用 Comparator 的形式是在 Dog class 内嵌套另一个 class 去 implements the Comparator 接口

public interface Comparator<T> {    int compare(T o1, T o2);}

其规定与 compareTo() 相似:

  • Return negative number if o1 < o2.
  • Return 0 if o1 equals o2.
  • Return positive number if o1 > o2.
import java.util.Comparator;public class Dog implements Comparable<Dog> {    ...    public int compareTo(Dog uddaDog) {        return this.size - uddaDog.size;    }    public static class NameComparator implements Comparator<Dog> {        public int compare(Dog a, Dog b) {            return a.name.compareTo(b.name);        }    }}

因为嵌套类NameComparator并没有应用外部类 Dog 的任何成员变量(实例变量),因而能够改为 static 优化内存空间
在DogLauncher.java中调用的模式为:

Dog.NameComparator nc = new Dog.NameComparator();

然而古代Java代码格调并非这样调用,咱们思考将 class NameComparator 封装,批改为 private

    private static class NameComparator implements Comparator<Dog> {        public int compare(Dog a, Dog b) {            return a.name.compareTo(b.name);        }    }    public static Comparator<Dog> getNameComparator() {        return new NameComparator();    }

如此一来在DogLauncher.java中调用则:

Comparator<Dog> cd = new Dog.NameComparator();if (cd.compare(d1, d3) > 0) {    d1.bark();} else {    d3.bark();}

作用是一样的,后者为古代code格调

接口为咱们提供了进行函数回调的能力。

  • 有时一个函数须要另一个可能还没有被写进去的函数的帮忙。
    • 例如:max须要compareTo
    • 这个辅助函数有时被称为 "回调"。
  • 有些语言应用显式函数传递来解决这个问题,例如python,javascript。
  • 在Java中,咱们通过将所需的函数封装在一个接口中来实现(例如,Arrays.sort须要compare,它位于comparator接口中)。
  • Arrays.sort在须要比拟的时候 "回调"。
    • 相似于在他人须要信息时把你的号码给他们。