这种一个 object variable 可以 refer to 多个类型的情况就叫 polymorphism (多态)
我们说object variables are polymorphic

⚠️ 只能父类定义子类 (好几层下面的也行),反过来不行得 cast。经测试,对类使用 getClass() 出的是 actual type

Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
boss.setBonus(5000);
var staff = new Employee[3];
staff[0] = boss;
staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);
for (Employee e : staff) 
	System.out.println(e.getName() + " " + e.getSalary());

But… Some Restrictions to Polymorphism

借用下面删除线的理解:说到底还是按声明的走,object variable 只能调用声明类型中有的东西,而对于那些重写过的——“动态绑定”

也因此,虽然 staff[0] 指向一个 Manager obj,但却没法 Manager m = staff[0] 我猜这说明了内部不是单纯的指向同一个 obj 这么简单

理解上可以按照 “说到底它还是一个 Employee 类” 或者 “employee 不能是 manager”。我推荐前者

现在再考虑为什么只能父类声明子类就合理多了,因为各干各的事,父类干的都是子类有的,如果反过来,子类干特殊的事父类没有,那还得了

Bad Things Happens When Using Polymorphism in Arrays

Manager[] managers = new Manager[10];
Employee[] staff = managers; // OK
staff[0] = new Employee("Jin Hailin", 1000000, 2025, 7, 1);
managers[0].setBonus(1000);

把子类数组给父类数组,这当然可以,就是用父类声明子类的行为嘛,但是要注意

  • 父类数组和子类数组都指向了同一个子类数组
  • 自己的类干自己的事情

因此

  1. 我用父类数组第一个元素 new 一个新父类对象,合理吗?合理
  2. 但是父类数组其实指向的是一个子类数组,合理吗?本来就是
  3. 那我用子类数组第一个元素的方法,没毛病吧?各干各的事嘛
  4. 但实际上第一个元素是父类对象吧,操作出问题吧?出问题了

没错,这样就会访问到一个不存在的 instance field,从而污染了别的 field

不过我实操这样会有 ArrayStoreException

Understanding Method Calls

  1. 编译器先看看这个 declared type 和方法名。编译器把本类中所有的和父类中 accessible candidates 枚举出来,先做一个 overload resolution 的大动作 (注意,还没 call 哟)

  2. 如果这个 resolution is private, static, final or constructor,那就开 call。这个过程叫 static binding 解读一下:能用 private 方法的能有谁?好像只有自己这个类吧,经测试,就是使用的当前方法所在类的对应私有方法。而 static 搞不清楚,确实是就算多态的情况下,也是按照声明的类型中的静态方法调用 但要是是别的情况,那就要按照 actual type 来调用函数了 (找不到就往父层找) 找了就开 call,这个过程叫 dynamic binding

It would be time-consuming to carry out this search every time a method is called. Instead, the virtual machine precomputes for each class a method table that lists all method signatures and the actual methods to be called.

If a method is not overridden, and it is short, then a compiler can optimize the method call away—a process called inlining.

Dynamic Binding

而这种能够正确的选出对应的方法的情况就叫 dynamic binding

知道为什么这么个东西还要命个名给它一个好像很牛逼的动态绑定了,因为这好像确实是个挺牛逼的东西。根据意思,这个多态说到底始终还是个父类,能调用的也只有父类有的方法,虽然 refer to 一个子类,但是子类的东西它是一个也不能调用,那按照这个道理,当调用被 override 的方法时,应该是父类的呀,结果它又能动态绑定子类了😅

C++ Note review

In C++, you need to declare a member function as virtual if you want dynamic binding. In Java, dynamic binding is the default behavior; if you do not want a method to be virtual, you tag it as final. (We discuss the final keyword later in this chapter.)

A Very Important Property

It makes programs extensible without the need for modifying existing code.

不是很能听得懂,他的意思好像是这个 obj variable 随便指,指到谁都能正确 call,且不需要重新编译代码

Casting

为什么要在 polymorphism 中提到 casting 呢?因为这里书中重点提的是 class 之间的 casting。所以build-in type 的 casting 就不在这里提了

class 中的 casting 只能发生在 inheritance hierarchy 中,并且一定是当我们把子类赋值给父类的时候 (即使用了多态的情况下),想要用这个子类的方法时 (promise more),就要把它 cast 回去

换句话说,还给它原本应有的能力。也因此,纯父类没法 cast into its subclass,此时会发生 ClassCastException

ask yourself whether this is an indication of a design flaw in the superclass.

In general, it is best to minimize the use of casts and the instanceof operator.

instance of

instance of 是一个 operator 用于判断这个实例是不是这个类或其子类出来的 (此时是透过“声明”看本质)

if (staff[1] instanceof Manager) {
	boss = (Manager) staff[1];
}

⚠️ 左边一定要是个对象,基础类型不行。并且左右两个要在一个继承树下

C++ Note review

p225