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

多态参考:概述

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

Employee employee;

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

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

创建多态

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

继承多态

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

 Employee employee;
Object object;
employee = new Employee();
object = employee; // This is a valid assignment

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

 Employee employee;
Object object = new Object();
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中的快速实现。

 package org.mano.example;
public class Company
{
public static void main( String[] args )
{
Payroll payroll = new Payroll();
payroll.paySalary();
}
}
package org.mano.example;
import java.util.ArrayList;
import java.util.List;
public class Payroll {
private List<Employee> employees =
new ArrayList<>();
public Payroll() {
employees.add(new
SalariedEmployee("Harry Potter",
"123-234-345",7800));
employees.add(new
CommissionEmployee("Peter Parker",
"234-345-456",2345.67,0.15));
employees.add(new
HourlyEmployee("Joker Poker",
"456-567-678",562.36,239.88));
}
public void paySalary() {
for (Employee e: employees) {
System.out.println
("----------------------------------------------------");
System.out.println(e.toString());
System.out.printf
("Gross payment: $%,.2f\n",e.salary());
System.out.println
("----------------------------------------------------");
}
}
} package org.mano.example; public abstract class Employee {
protected String name;
protected String ssn;
public Employee(String name, String ssn) {
this.name = name;
this.ssn = ssn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
@Override
public String toString() {
return String.format("%s\nSSN: %s",
getName(),getSsn());
}
public abstract double salary();
} package org.mano.example;
public class SalariedEmployee extends Employee {
protected double basicSalary;
public SalariedEmployee(String name, String ssn,
double basicSalary) {
super(name, ssn);
setBasicSalary(basicSalary);
}
public double getBasicSalary() {
return basicSalary;
}
public void setBasicSalary(double basicSalary) {
if(basicSalary>= 0.0)
this.basicSalary = basicSalary;
else
throw new IllegalArgumentException("basic " +
"salary must be greater than 0.0");
}
@Override
public double salary() {
eturn getBasicSalary();
}
@Override
public String toString() {
return String.format("%s\nBasic Salary: $%,.2f",
super.toString(),getBasicSalary());
}
} package org.mano.example;
public class HourlyEmployee extends Employee {
protected double wage;
protected double hours;
public HourlyEmployee(String name, String ssn,
double wage, double hours) {
super (name, ssn);
setWage(wage);
setHours(hours);
}
public double getWage() {
return wage;
}
public void setWage(double wage) {
if(wage >= 0.0)
this.wage = wage;
else
throw new IllegalArgumentException("wage " +
"must be > 0.0");
}
public double getHours() {
return hours;
}
public void setHours(double hours) {
if(hours >= 0.0)
this.hours = hours;
else
throw new IllegalArgumentException("hours " +
"must be > 0.0");
}
@Override
public double salary() {
return getHours() * getWage();
}
@Override
public String toString() {
return String.format("%s\nWage: $%,
.2f\nHours worked: %,.2f",
super.toString(),getWage(),getHours());
}
} package org.mano.example;
public class CommissionEmployee extends Employee {
protected double sales;
protected double commission;
public CommissionEmployee(String name, String ssn,
double sales, double commission) {
super(name, ssn);
setSales(sales);
setCommission(commission);
}
public double getSales() {
return sales;
}
public void setSales(double sales) {
if(sales >=0.0)
this.sales = sales;
else
throw new IllegalArgumentException("Sales " +
"must be >= 0.0");
}
public double getCommission() {
return commission;
}
public void setCommission(double commission) {
if(commission > 0.0 && commission < 1.0)
this.commission = commission;
else
throw new IllegalArgumentException("Commission " +
"must be between 0.0 and 1.0");
}
@Override
public double salary() {
return getCommission() * getSales();
}
@Override
public String toString() {
return String.format("%s\nSales: %,
.2f\nCommission: %,.2f",
super.toString(),getSales(),getCommission());
}
}

接口多态

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

 package org.mano.example;
public interface Player {
public enum STATUS{PLAY,PAUSE,STOP};
public void play();
public void stop();
public void pause();
} package org.mano.example;
public class VideoPlayer implements Player {
private STATUS currentStatus = STATUS.STOP;
@Override
public void play() {
if(currentStatus == STATUS.STOP ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.PLAY;
System.out.println("Playing Video...");
}
else
System.out.println("I am ON playing man!");
}
@Override
public voidstop() {
if(currentStatus == STATUS.PLAY ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.STOP;
System.out.println("Video play stopped.");
}
else
System.out.println("Do you want me to go fishing?");
}
@Override
public void pause() {
if(currentStatus == STATUS.PLAY) {
currentStatus = STATUS.PAUSE;
System.out.println("Video play paused.");
}
else
System.out.println("I'm a statue. You froze me
already!");
}
} package org.mano.example;
public class AudioPlayer implements Player {
private STATUS currentStatus = STATUS.STOP;
@Override
public void play() {
if(currentStatus == STATUS.STOP ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.PLAY;
System.out.println("Playing Audio...");
}
else
System.out.println("I am ON playing man!");
}
@Override
public void stop() {
if(currentStatus == STATUS.PLAY ||
currentStatus == STATUS.PAUSE) {
currentStatus = STATUS.STOP;
System.out.println("Audio play stopped.");
}
else
System.out.println("Do you want me to go fishing?");
}
@Override
public void pause() {
if(currentStatus == STATUS.PLAY) {
currentStatus = STATUS.PAUSE;
System.out.println("Audio play paused.");
}
else
System.out.println("I'm a statue. You froze me
already!");
}
} package org.mano.example;
public class PlayerApp {
public static void main(String[] args) {
Player player= new VideoPlayer();
player.play();
player.pause();
player.stop();
player= new AudioPlayer();
player.play();
player.pause();
player.stop();
}
}

请注意,在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. css字体图标的制作

    我介绍的这个办法是直接在 "阿里巴巴图标库"中制作的,方便快捷 1. 首先到 "阿里巴巴图标库"中找到你想要的图片,然后选择加入购物车 接着我们点击右上角的购物 ...

  2. nyoj 38-布线问题(prim, sort)

    38-布线问题 内存限制:64MB 时间限制:1000ms Special Judge: No accepted:5 submit:11 题目描述: 南阳理工学院要进行用电线路改造,现在校长要求设计师 ...

  3. Composer依赖管理 – PHP的利器

    别再到处搜PHP类扩展包了,对于现代语言而言,包管理器基本上是标配.Java 有 Maven,Python 有 pip,Ruby 有 gem,Nodejs 有 npm.PHP 的则是 PEAR,不过 ...

  4. <automate the boring stuff with python>---第七章 正则实例&正则贪心&匹配电话号码和邮箱

    第七章先通过字符串查找电话号码,比较了是否使用正则表达式程序的差异,明显正则写法更为简洁.易扩展.模式:3 个数字,一个短横线,3个数字,一个短横线,再是4 个数字.例如:415-555-4242 i ...

  5. IPv6,无需操作就可升级?

    最近这段时间,5G 出现在你能看到的各种信息里,铺天盖地的宣传提醒着大家新一代互联网的到来.其实早在几年前 5G 就有所提及,可是为什么到现在才开始窜上热门呢?这就涉及到了 IPv6. 或许有不少朋友 ...

  6. robatframework+jenkins+email集成部署方案

    准备工作: 1.jenkins.war包 下载地址:https://jenkins.io/zh/download/ 2.Jdk1.8 下载地址:http://www.oracle.com/techne ...

  7. 【故障公告】数据库服务器 CPU 近 100% 引发的故障

    抱歉,今天上午 10:48 ~ 10:33 期间,我们所使用的数据库服务(阿里云 RDS 实例 SQL Server 2016 标准版)又出现了 CPU 近 100% 问题,由此给您带来麻烦,请您谅解 ...

  8. cenos7搭建gitlab

    git.github和gitlab的区别 git:是一种版本控制系统,是一个命令,是一种工具 gitlib:是基于实现功能的开发库 github:是一个基于git实现的在线代码仓库软件 gitlib可 ...

  9. linux [CTRL]+c与[CTRL]+d

    [CTRL]+c:中断目前程序.用于在linux中输入了错误的命令或者参数,有的时候会在系统不停的运行,如果想让程序需停下来,可以使用[CTRL]+C [CTRL]+d:这个组合键代表着键盘输入结束( ...

  10. 请求https前缀的网站验证SSL证书的解决方案之一

    from requests.packages.urllib3.exceptions import InsecureRequestWarning # 禁用安全请求警告requests.packages. ...