多态是面向对象软件的基本原理之一。该术语通常表示可以具有多种形式的事物。在面向对象的方法中,多态使编写具有后期绑定引用的程序成为可能。尽管在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. T-SQL Part X: UNION, EXCEPT and INTERSECT

    MSDN上关于EXCEPT和INTERSECT的文档.MSDN上关于UNION的文档. 值得注意的是,UNION其实有两种,一种是普通的UNION,另外一种是UNION ALL.加上EXCEPT和IN ...

  2. thinkphp6.0 开启调试模式以及Driver [Think] not supported

    thinkphp6.0 开启调试模式 首先确认自己是通过 composer 进行的下载,然后修改系统目录下的 .example.env 为 .env 文件 修改 config->app.php ...

  3. BootStrap中的collapse插件堆叠效果

    通过网络上的一系列查找,总结出的collapse插件堆叠效果(网上没有找到,只能自己弄了,帮助那些和我遇到一样状况的同学) 首先感谢一位网友的知识总结给了我灵感,在这里先帮他推荐一波(https:// ...

  4. ubuntukylin16.04LTS(乌班图麒麟版长期支持版,并非银河麒麟)安装体验

    最近,国产银河麒麟版在政府部门推广使用.我有幸接触了,感觉还是不错的.这次政府软件正版化整改中,也列入了windows和银河麒麟的选项.我想试安装一下,可是没找到.就近找了它的类似系统ubuntuky ...

  5. react一写工具

    动画库:React-transition-group ui框架:Ant Design

  6. 快速遍历OpenCV Mat图像数据的多种方法和性能分析 | opencv mat for loop

    本文首发于个人博客https://kezunlin.me/post/61d55ab4/,欢迎阅读! opencv mat for loop Series Part 1: compile opencv ...

  7. 【如何让代码变“高级”(一)】-Spring组合注解提升代码维度

    原创不易,点个赞

  8. nginx 负载均衡简单配置

    配置要求: 三台服务器 127.0.0.1       主负载(把访问请求分给主机池) 127.0.0.2       主机2 127.0.0.3       主机3 第一步: 配置127.0.0.1 ...

  9. 手把手教学h5小游戏 - 贪吃蛇

    简单的小游戏制作,代码量只有两三百行.游戏可自行扩展延申. 源码已发布至github,喜欢的点个小星星,源码入口:game-snake 游戏已发布,游戏入口:http://snake.game.yan ...

  10. Spring源码分析之AOP

    1.AOP简介 AOP即面向切面编程(Aspect Oriented Programming),通过预编译方式及运行期动态代理实现程序功能的统一维护的一种技术.使用aop对业务逻辑的各个部分进行隔离, ...