编译看左边,运行看右边

多态是一项“将改变的事物与不变的事物分离”的重要技术。

类对象可以赋值给父类引用变量,这叫多态;实际执行调用的是子类实现,这叫动态绑定。

多态的形成条件

  1. Java引用变量有两个类型:一个是编译时类型,一个是运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就可能出现多态(Polymorphism)。

向上转型

父类 a = 子类对象,可以将子类看作是一种特殊的父类,因此Java允许把一个子类对象直接赋给一个父类引用变量,无须任何类型转换,或者被称为向上转型(upcasting),向上转型由系统自动完成。

a 变量编译时类型是父类,而运行时类型是子类。当运行时调用该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,这就可能出现:相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态。

向下转型

_引用变量的强制类型转换

  1. 子类 a = 父类对象,编写Java程序时,引用变量只能调用它编译时类型的方法,而不能调用它运行时类型的方法,即使它实际所引用的对象确实包含该方法。如果需要让这个引用变量调用它运行时类型的方法,则必须把它强制类型转换成运行时类型,强制类型转换需要借助于类型转换运算符。<br /> <br /> 类型转换运算符是小括号,类型转换运算符的用法是:(type)variable,这种用法可以将variable变量转换成一个type类型的变量。
  2. 向下转型需要注意以下两点:
  1. 基本类型之间的转换只能在数值类型之间进行,这里所说的数值类型包括整数型、字符型和浮点型。但数值类型和布尔类型之间不能进行类型转换。

  2. 引用类型之间的转换只能在具有继承关系的两个类型之间进行,如果是两个没有任何继承关系的类型,则无法进行类型转换,否则编译时就会出现错误。如果试图把一个父类实例转换成子类类型,则这个对象必须实际上是子类实例才行(即编译时类型为父类类型,而运行时类型是子类类型),否则将在运行时引发ClassCastException异常。

注意

  1. 只有普通的方法调用可以是多态的,对象中的实例变量是不具备多态性的。

例如,如果你直接访问一个属性,该访问会在编译时解析

  1. 通过引用变量来访问其包含的实例变量时,系统总是试图访问它编译时类型所定义的成员变量,而不是它运行时类型所定义的成员变量。

多态的意义

提高代码的可扩展性和复用性

示例代码如下:

  1. //接口类,定义了一个可以遍历集合数据的迭代器
  2. public interface Iterator {
  3. String hasNext();
  4. String next();
  5. String remove();
  6. }
  7. public class Array implements Iterator {
  8. private String[] data;
  9. public String hasNext() { ... }
  10. public String next() { ... }
  11. public String remove() { ... }
  12. //...省略其他方法...
  13. }
  14. public class LinkedList implements Iterator {
  15. private LinkedListNode head;
  16. public String hasNext() { ... }
  17. public String next() { ... }
  18. public String remove() { ... }
  19. //...省略其他方法...
  20. }
  21. public class Demo {
  22. private static void print(Iterator iterator) {
  23. while (iterator.hasNext()) {
  24. System.out.println(iterator.next());
  25. }
  26. }
  27. public static void main(String[] args) {
  28. Iterator arrayIterator = new Array();
  29. print(arrayIterator);
  30. Iterator linkedListIterator = new LinkedList();
  31. print(linkedListIterator);
  32. }
  33. }
  1. 如上面代码所示,我们利用多态的特性,仅用一个 print() 函数就可以实现遍历打印不同类型(ArrayLinkedList)集合的数据。当再增加一种要遍历打印的类型的时候,比如 HashMap,我们只需让 HashMap 实现 Iterator 接口,重新实现自己的 hasNext()、next() 等方法就可以了,完全不需要改动 print() 函数的代码。所以说,多态提高了代码的可扩展性。

如果我们不使用多态特性,我们就无法将不同的集合类型(Array、LinkedList)传递给相同的函数(print(Iterator iterator) 函数)。我们需要针对每种要遍历打印的集合,分别实现不同的 print() 函数,比如针对 Array,我们要实现 print(Array array) 函数,针对 LinkedList,我们要实现 print(LinkedList linkedList) 函数。而利用多态特性,我们只需要实现一个 print() 函数的打印逻辑,就能应对各种集合数据的打印操作,这显然提高了代码的复用性。