多态是面向对象软件的基本原理之一。该术语通常表示可以具有多种形式的事物。在面向对象的方法中,多态使编写具有后期绑定引用的程序成为可能。尽管在Java中创建多态引用很容易,但其背后的概念对整体编程产生了更深远的影响。本文结合在优锐课学习到的知识点,探讨了有关多态性及其对面向对象编程的影响的一些复杂细节。

多态参考:概述

多态引用是一个变量,可以在不同的时间点引用不同类型的对象。它通常与它所引用的类兼容。 例如,在以下情况下:

  1. Employee employee;

'employee'是一个引用变量,可以引用Employee类的实例。参考变量对参考对象的限定取决于其兼容性。这似乎是唯一可靠的条件,但事实并非如此,特别是在实现多态时。规则太严格了,但是通过结合“具有多种形式”的思想,多态性使得灵活性更高。这意味着多态引用保证了它可以在不同的时间点引用不同类型的对象,而不是为兼容性而完全依赖。因此,如果可以在一个时间点使用引用来调用方法,则可以将其动态更改为指向另一个对象,并在下一次调用其他方法。通过提供引用变量的另一个使用维度,这可以利用灵活性。

当引用变量绑定到无法在运行时更改的对象时,或者换句话说,方法调用与方法定义的绑定是在编译时完成的,称为静态绑定。如果绑定在运行时是可更改的,例如在多态引用的情况下,绑定的决策仅在执行期间做出,则称为动态绑定或后期绑定。两者在面向对象程序设计中都有其用途,并非一个都胜过另一个。但是,在多态引用的情况下,推迟的绑定承诺在灵活性方面使其比编译时绑定更具优势,但另一方面,这会降低性能开销。但是,这在很大程度上是可以接受的,并且在提高效率方面,开销通常具有很小的吸引力。.

创建多态

在Java中,可以通过两种方式创建多态引用:使用继承或使用接口。

继承多态

引用变量引用类的实例。对于继承层次结构,如果引用变量在层次结构树中声明为父类类型,则引用对象可以指向层次结构中任何类的实例。这意味着,在Java中,因为Objectclass是所有类的父类或超类,或者换句话说,Java中的所有类实际上是Object类的子类隐式或显式地是对象类的引用变量。对象类型可以引用Java中的任何类实例。这就是我们的意思。

  1. Employee employee;
  2. Object object;
  3. employee = new Employee();
  4. object = employee; // This is a valid assignment

如果情况相反,则如下所示:

  1. Employee employee;
  2. Object object = new Object();
  3. employee = (Employee)object // Valid, but needs explicit cast

观察到它需要显式强制转换;只有这样,它才能成为有效的声明。可以看出,这种反向分配对于在许多情况下出现问题的边缘来说没有多大用处。这是因为Object实例的功能与Employee引用变量预期的功能几乎没有关系。关系is-a可以从employee-is-an-object派生派生;在这种情况下,相反的关系(例如,对象是雇员)太牵强。

继承多态性:一个例子

让我们尝试借助示例来理解它。

1:从驱动程序类,公司派生的类

称为Company的驱动程序类创建一个雇员列表,并调用paySalary()方法。薪资类维护公司中不同类型员工的列表。请注意,该数组被声明为派生自Employee类(所有雇员子类的父级或超类)的引用变量的数组。结果,可以用从Employee类的任何子类(例如CommissionEmployeeHourlyEmployee, SalariedEmployee)创建的对象引用填充数组。在paySalary()定义中,根据数组中的对象引用调用适当的salary()方法。因此,对salary()方法的调用是多态的,很明显,每个类都有其自己版本的salary()方法。

Payroll类中的employee数组不代表特定类型的Employee。它用作可以指向任何类型的Employee子类引用的句柄。尽管继承的类共享作为后代继承的一些公共数据,但是它们具有各自的属性集。

通过继承实现多态:Java实现

这是该示例在Java中的快速实现。

  1. package org.mano.example;
  2. public class Company
  3. {
  4. public static void main( String[] args )
  5. {
  6. Payroll payroll = new Payroll();
  7. payroll.paySalary();
  8. }
  9. }
  10. package org.mano.example;
  11. import java.util.ArrayList;
  12. import java.util.List;
  13. public class Payroll {
  14. private List<Employee> employees =
  15. new ArrayList<>();
  16. public Payroll() {
  17. employees.add(new
  18. SalariedEmployee("Harry Potter",
  19. "123-234-345",7800));
  20. employees.add(new
  21. CommissionEmployee("Peter Parker",
  22. "234-345-456",2345.67,0.15));
  23. employees.add(new
  24. HourlyEmployee("Joker Poker",
  25. "456-567-678",562.36,239.88));
  26. }
  27. public void paySalary() {
  28. for (Employee e: employees) {
  29. System.out.println
  30. ("----------------------------------------------------");
  31. System.out.println(e.toString());
  32. System.out.printf
  33. ("Gross payment: $%,.2f\n",e.salary());
  34. System.out.println
  35. ("----------------------------------------------------");
  36. }
  37. }
  38. }
  39.  
  40. package org.mano.example;
  41.  
  42. public abstract class Employee {
  43. protected String name;
  44. protected String ssn;
  45. public Employee(String name, String ssn) {
  46. this.name = name;
  47. this.ssn = ssn;
  48. }
  49. public String getName() {
  50. return name;
  51. }
  52. public void setName(String name) {
  53. this.name = name;
  54. }
  55. public String getSsn() {
  56. return ssn;
  57. }
  58. public void setSsn(String ssn) {
  59. this.ssn = ssn;
  60. }
  61. @Override
  62. public String toString() {
  63. return String.format("%s\nSSN: %s",
  64. getName(),getSsn());
  65. }
  66. public abstract double salary();
  67. }
  68.  
  69. package org.mano.example;
  70. public class SalariedEmployee extends Employee {
  71. protected double basicSalary;
  72. public SalariedEmployee(String name, String ssn,
  73. double basicSalary) {
  74. super(name, ssn);
  75. setBasicSalary(basicSalary);
  76. }
  77. public double getBasicSalary() {
  78. return basicSalary;
  79. }
  80. public void setBasicSalary(double basicSalary) {
  81. if(basicSalary>= 0.0)
  82. this.basicSalary = basicSalary;
  83. else
  84. throw new IllegalArgumentException("basic " +
  85. "salary must be greater than 0.0");
  86. }
  87. @Override
  88. public double salary() {
  89. eturn getBasicSalary();
  90. }
  91. @Override
  92. public String toString() {
  93. return String.format("%s\nBasic Salary: $%,.2f",
  94. super.toString(),getBasicSalary());
  95. }
  96. }
  97.  
  98. package org.mano.example;
  99. public class HourlyEmployee extends Employee {
  100. protected double wage;
  101. protected double hours;
  102. public HourlyEmployee(String name, String ssn,
  103. double wage, double hours) {
  104. super (name, ssn);
  105. setWage(wage);
  106. setHours(hours);
  107. }
  108. public double getWage() {
  109. return wage;
  110. }
  111. public void setWage(double wage) {
  112. if(wage >= 0.0)
  113. this.wage = wage;
  114. else
  115. throw new IllegalArgumentException("wage " +
  116. "must be > 0.0");
  117. }
  118. public double getHours() {
  119. return hours;
  120. }
  121. public void setHours(double hours) {
  122. if(hours >= 0.0)
  123. this.hours = hours;
  124. else
  125. throw new IllegalArgumentException("hours " +
  126. "must be > 0.0");
  127. }
  128. @Override
  129. public double salary() {
  130. return getHours() * getWage();
  131. }
  132. @Override
  133. public String toString() {
  134. return String.format("%s\nWage: $%,
  135. .2f\nHours worked: %,.2f",
  136. super.toString(),getWage(),getHours());
  137. }
  138. }
  139.  
  140. package org.mano.example;
  141. public class CommissionEmployee extends Employee {
  142. protected double sales;
  143. protected double commission;
  144. public CommissionEmployee(String name, String ssn,
  145. double sales, double commission) {
  146. super(name, ssn);
  147. setSales(sales);
  148. setCommission(commission);
  149. }
  150. public double getSales() {
  151. return sales;
  152. }
  153. public void setSales(double sales) {
  154. if(sales >=0.0)
  155. this.sales = sales;
  156. else
  157. throw new IllegalArgumentException("Sales " +
  158. "must be >= 0.0");
  159. }
  160. public double getCommission() {
  161. return commission;
  162. }
  163. public void setCommission(double commission) {
  164. if(commission > 0.0 && commission < 1.0)
  165. this.commission = commission;
  166. else
  167. throw new IllegalArgumentException("Commission " +
  168. "must be between 0.0 and 1.0");
  169. }
  170. @Override
  171. public double salary() {
  172. return getCommission() * getSales();
  173. }
  174. @Override
  175. public String toString() {
  176. return String.format("%s\nSales: %,
  177. .2f\nCommission: %,.2f",
  178. super.toString(),getSales(),getCommission());
  179. }
  180. }

接口多态

接口的多态性与前面的示例非常相似,不同之处在于,这里的多态性规则是根据Java接口指定的规范进行的。接口名称可以用作引用变量,就像我们对上面的类名称所做的那样。它引用实现该接口的任何类的任何对象。这是一个例子。

  1. package org.mano.example;
  2. public interface Player {
  3. public enum STATUS{PLAY,PAUSE,STOP};
  4. public void play();
  5. public void stop();
  6. public void pause();
  7. }
  8.  
  9. package org.mano.example;
  10. public class VideoPlayer implements Player {
  11. private STATUS currentStatus = STATUS.STOP;
  12. @Override
  13. public void play() {
  14. if(currentStatus == STATUS.STOP ||
  15. currentStatus == STATUS.PAUSE) {
  16. currentStatus = STATUS.PLAY;
  17. System.out.println("Playing Video...");
  18. }
  19. else
  20. System.out.println("I am ON playing man!");
  21. }
  22. @Override
  23. public voidstop() {
  24. if(currentStatus == STATUS.PLAY ||
  25. currentStatus == STATUS.PAUSE) {
  26. currentStatus = STATUS.STOP;
  27. System.out.println("Video play stopped.");
  28. }
  29. else
  30. System.out.println("Do you want me to go fishing?");
  31. }
  32. @Override
  33. public void pause() {
  34. if(currentStatus == STATUS.PLAY) {
  35. currentStatus = STATUS.PAUSE;
  36. System.out.println("Video play paused.");
  37. }
  38. else
  39. System.out.println("I'm a statue. You froze me
  40. already!");
  41. }
  42. }
  43.  
  44. package org.mano.example;
  45. public class AudioPlayer implements Player {
  46. private STATUS currentStatus = STATUS.STOP;
  47. @Override
  48. public void play() {
  49. if(currentStatus == STATUS.STOP ||
  50. currentStatus == STATUS.PAUSE) {
  51. currentStatus = STATUS.PLAY;
  52. System.out.println("Playing Audio...");
  53. }
  54. else
  55. System.out.println("I am ON playing man!");
  56. }
  57. @Override
  58. public void stop() {
  59. if(currentStatus == STATUS.PLAY ||
  60. currentStatus == STATUS.PAUSE) {
  61. currentStatus = STATUS.STOP;
  62. System.out.println("Audio play stopped.");
  63. }
  64. else
  65. System.out.println("Do you want me to go fishing?");
  66. }
  67. @Override
  68. public void pause() {
  69. if(currentStatus == STATUS.PLAY) {
  70. currentStatus = STATUS.PAUSE;
  71. System.out.println("Audio play paused.");
  72. }
  73. else
  74. System.out.println("I'm a statue. You froze me
  75. already!");
  76. }
  77. }
  78.  
  79. package org.mano.example;
  80. public class PlayerApp {
  81. public static void main(String[] args) {
  82. Player player= new VideoPlayer();
  83. player.play();
  84. player.pause();
  85. player.stop();
  86. player= new AudioPlayer();
  87. player.play();
  88. player.pause();
  89. player.stop();
  90. }
  91. }

请注意,在PlayerApp中,我们已经使用接口Player来声明对象引用变量。引用变量player可以引用实现Player接口的任何类的任何对象。为了证明这一点,我们在这里使用了同一播放器变量来引用VideoPlayer对象和AudioPlayer对象。运行时调用的方法是特定于其引用的类对象的方法。实现接口的类与接口本身之间的关系是父子关系,正如我们在带有继承的多态示例中所看到的。它也是一个is-arelationship,并构成了多态性的基础。

总结

通过类继承或通过接口实现多态性之间的差异是一个选择问题。实际上,区别在于理解类和接口的属性和特性。除了了解其性质外,没有严格的规则来定义何时使用。这超出了本文的范围。但是,在多态中,这个想法既适合并且也有能力完成我们想对它们进行的操作。就这样。抽丝剥茧,细说架构那些事——【优锐课】

深入探讨多态性及其在Java中的好处的更多相关文章

  1. Java中多态性的实现

    class A ...{ public String show(D obj)...{ return ("A and D"); } public String show(A obj) ...

  2. Java中对域和静态方法的访问不具有多态性

    1.将方法调用同方法主体关联起来被称为 2.编译期绑定(静态)是在程序编译阶段就确定了引用对象的类型 3.运行期绑定(动态绑定)是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法 ...

  3. 转载 - java中接口的向上转型。和多态性

    发现一篇对接口总结很精简的文章 1.在java中接口就是一个完全抽象的类,跟抽象类一样不能产生对象,但是可以作为对象的引用,可以由其实现类向上转型,它就跟超类一样, 向上转型了,可以很好的利用接口,可 ...

  4. 【Java_基础】java中的多态性

    方法的重载.重写和动态链接构成了java的多态性. 1.方法的重载 同一个类中多个同名但形参有所差异的方法,在调用时会根据参数的不同做出选择. 2.方法的重写 子类中重新定义了父类的方法,有关方法重写 ...

  5. 深入探讨Java中的异常与错误处理

    Java中的异常处理机制已经比较成熟,我们的Java程序到处充满了异常的可能,如果对这些异常不做预先的处理,那么将来程序崩溃就无从调试,很难找到异常所在的位置.本文将探讨一下Java中异常与错误的处理 ...

  6. 深入Java核心 Java中多态的实现机制(1)

    在疯狂java中,多态是这样解释的: 多态:相同类型的变量,调用同一个方法时,呈现出多中不同的行为特征, 这就是多态. 加上下面的解释:(多态四小类:强制的,重载的,参数的和包含的) 同时, 还用人这 ...

  7. java中实现多态的机制是什么?

    多态性是面向对象程序设计代码重用的一个重要机制,我们曾不只一次的提到Java多态性.在Java运行时多态性:继承和接口的实现一文中,我们曾详细介绍了Java实现运行时多态性的动态方法调度:今天我们再次 ...

  8. Java中hashcode的理解

    Java中hashcode的理解 原文链接http://blog.csdn.net/chinayuan/article/details/3345559 怎样理解hashCode的作用: 以 java. ...

  9. Java中堆内存和栈内存详解2

    Java中堆内存和栈内存详解   Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,ja ...

随机推荐

  1. JS简单循环遍历json数组的方法

    例如数据库里面的json字符串是这样的 1 2 3 4 5 var str = '[{"name":"宗2瓜","num":"1& ...

  2. 微擎框架商业版 V2.1.2 去后门一键安装版+去除云平台+无附带模块

    下载地址:http://dd.ma/AdVvoDu5 关注微信公众号codervip,点击公众号菜单,获取提取码! 这个是一键安装版本,所以微擎安装比较简单,不用大家手动去改数据库了,而且修复上个2. ...

  3. 回声消除中的LMS和NLMS算法与MATLAB实现

    自适应滤波是数字信号处理的核心技术之一,在科学和工业上有着广泛的应用领域.自适应滤波技术应用广泛,包括回波抵消.自适应均衡.自适应噪声抵消和自适应波束形成.回声对消是当今通信系统中普遍存在的现象.声回 ...

  4. nyoj 412 Same binary weight ()

    Same binary weight 时间限制:300 ms  |  内存限制:65535 KB 难度:3   描述 The binary weight of a positive  integer ...

  5. PHP文件上传和下载

    第 1 章 文件上传 1.1 客户端上传设置 在 B/S 程序中文件上传已经成为一个常用功能.其目的是客户可以通过浏览器 (Browser) 将文件上传到服务器(Server)上的指定目录. 网络上常 ...

  6. 学习记录:《C++设计模式——李建忠主讲》6.“状态变化”模式

    状态变化模式:在组件构建过程中,某些对象的状态经常面临变化,如何对这些变化进行有效的管理?同时又维持高层模块的稳定.状态变化模式为这一问题提供了一种解决方案. 典型模式:状态模式(State).备忘录 ...

  7. 十、GAP

    1.1     背景 GAP(Generic Access Profile)位于主机协议栈的最顶层,用来定义BLE设备在待机或者连接状态中的行为,该Profile保证不同的Bluetooth产品可以互 ...

  8. 2018.3.7java 学习第二天

    java 学习的第二天,首先我们要学习的是声明变量,在程序中,我们必须先声明变量,然后才可以去使用之,那么变量究竟是什么呢? 在很多课本上,有很多的定义诸如:“变量来源于数学,是计算机语言中能储存计算 ...

  9. GeoServer 发布的图层预览不了

    说明: 在用Geoserver发布Postgis发布的图层后,去LayerPreview中预览,但是选openlayers时,没有跳转到预览页面,而是弹出了下载WMS. 解决方案: 用KML方式预览时 ...

  10. python线程条件变量Condition(31)

    对于线程与线程之间的交互我们在前面的文章已经介绍了 python 互斥锁Lock / python事件Event , 今天继续介绍一种线程交互方式 – 线程条件变量Condition. 一.线程条件变 ...