Java中的多重继承与组合vs继承

有时我写了几篇有关Java 继承接口组成的文章。 在这篇文章中,我们将研究多重继承,然后学习组成优于继承的好处。

Java中的多重继承

多重继承是创建具有多个超类的单个类的能力。 与其他一些流行的面向对象的编程语言(例如C ++)不同, java不提供对类中多重继承的支持 。 Java不支持类中的多重继承,因为它可能导致菱形问题 ,而不是提供解决复杂问题的方法,还有更好的方法来实现与多重继承相同的结果。

钻石问题

为了轻松理解钻石问题,我们假设Java支持多重继承。 在这种情况下,我们可以像下面的图像那样有一个类层次结构。

钻石问题多重继承

假设SuperClass是一个抽象类,声明了一些方法,而ClassA,ClassB是具体类。

超类.java

package com.journaldev.inheritance;public abstract class SuperClass {public abstract void doSomething();
}

ClassA.java

package com.journaldev.inheritance;public class ClassA extends SuperClass{@Overridepublic void doSomething(){System.out.println("doSomething implementation of A");}//ClassA own methodpublic void methodA(){}
}

ClassB.java

package com.journaldev.inheritance;public class ClassB extends SuperClass{@Overridepublic void doSomething(){System.out.println("doSomething implementation of B");}//ClassB specific methodpublic void methodB(){}
}

现在,让我们说ClassC的实现如下所示,它扩展了ClassA和ClassB。

ClassC.java

package com.journaldev.inheritance;public class ClassC extends ClassA, ClassB{public void test(){//calling super class methoddoSomething();}}

注意, test()方法正在调用超类doSomething()方法,这导致歧义,因为编译器不知道要执行哪个超类方法,并且由于菱形类图,它被称为Diamond Problem,这是Java不支持类中的多重继承的主要原因。

请注意,上述具有多类继承的问题也可能只出现在三个类中,它们全部具有至少一个通用方法。

接口中的多重继承

您可能已经注意到,我一直在说类不支持多重继承,但接口支持多重继承,并且单个接口可以扩展多个接口,下面是一个简单的示例。

接口A.java

package com.journaldev.inheritance;public interface InterfaceA {public void doSomething();
}

接口B.java

package com.journaldev.inheritance;public interface InterfaceB {public void doSomething();
}

注意,两个接口都声明了相同的方法,现在我们可以有一个扩展这两个接口的接口,如下所示。

接口C.java

package com.journaldev.inheritance;public interface InterfaceC extends InterfaceA, InterfaceB {//same method is declared in InterfaceA and InterfaceB bothpublic void doSomething();}

这样做非常好,因为接口仅声明方法,并且实际实现将由实现接口的具体类来完成,因此在接口的多重继承中不存在任何歧义的可能性。

这就是为什么Java类可以实现多重继承的原因,例如下面的示例。

接口Impl.java

package com.journaldev.inheritance;public class InterfacesImpl implements InterfaceA, InterfaceB, InterfaceC {@Overridepublic void doSomething() {System.out.println("doSomething implementation of concrete class");}public static void main(String[] args) {InterfaceA objA = new InterfacesImpl();InterfaceB objB = new InterfacesImpl();InterfaceC objC = new InterfacesImpl();//all the method calls below are going to same concrete implementationobjA.doSomething();objB.doSomething();objC.doSomething();}}

您是否注意到,每当我覆盖任何超类方法或实现任何接口方法时,我都使用@Override注释,它是三个内置的Java注释之一,并且在覆盖任何方法时都应始终使用覆盖注释 。

救援人员组成

因此,如果我们想在ClassC利用ClassA函数methodA()ClassB函数methodB() ,该解决方案在于使用composition ,这是ClassC的重构版本,该版本使用了composition来同时利用类方法和doSomething ()方法来自其中一个对象。

ClassC.java

package com.journaldev.inheritance;public class ClassC{ClassA objA = new ClassA();ClassB objB = new ClassB();public void test(){objA.doSomething();}public void methodA(){objA.methodA();}public void methodB(){objB.methodB();}
}

组合与继承

Java编程的最佳实践之一是“通过接口支持组合”,我们将研究一些偏爱这种方法的方面。

  1. 假设我们有一个超类和子类,如下所示:

    ClassC.java

    package com.journaldev.inheritance;public class ClassC{public void methodC(){}
    }

    ClassD.java

    package com.journaldev.inheritance;public class ClassD extends ClassC{public int test(){return 0;}
    }

    上面的代码可以编译并正常工作,但是如果ClassC实现更改如下,该怎么办:

    ClassC.java

    package com.journaldev.inheritance;public class ClassC{public void methodC(){}public void test(){}
    }

    请注意,子类中已经存在test()方法,但是返回类型有所不同,现在ClassD将无法编译,并且如果您使用的是任何IDE,它将建议您更改超类或子类中的返回类型。

    现在想象一下这样的情况:我们具有多个级别的类继承,并且超类不受我们控制,我们别无选择,只能更改子类方法签名或名称以消除编译错误,我们还必须在所有方面进行更改子类方法被调用的地方,因此继承使我们的代码易碎。

    组合永远不会发生上述问题,这使其比继承更有利。

  2. 继承的另一个问题是,我们将所有超类方法公开给客户端,并且如果我们的超类设计不当且存在安全漏洞,那么即使我们在实现类时全力以赴,我们也会受到不佳实现的影响。超类。
    组合可以帮助我们提供对超类方法的受控访问,而继承不提供对超类方法的任何控制,这也是组合优于继承的主要优势之一。
  3. 组合的另一个好处是它提供了方法调用的灵活性。 我们上ClassC实现不是最佳的,它提供了与将要调用的方法的编译时绑定,只需进行很小的更改,我们就可以灵活地使方法调用并使之动态。

    ClassC.java

    package com.journaldev.inheritance;public class ClassC{SuperClass obj = null;public ClassC(SuperClass o){this.obj = o;}public void test(){obj.doSomething();}public static void main(String args[]){ClassC obj1 = new ClassC(new ClassA());ClassC obj2 = new ClassC(new ClassB());obj1.test();obj2.test();}
    }

    上面程序的输出是:

    doSomething implementation of A
    doSomething implementation of B

    这种方法调用的灵活性在继承中不可用,从而提倡了最佳做法,即在继承方面偏向于组合。

  4. 单元测试很容易组合,因为我们知道超类中正在使用的所有方法,并且可以对其进行模拟,而在继承中,我们很大程度上依赖于超类,并且不知道将使用超类的所有方法,因此我们需要要测试超类的所有方法,这是一项额外的工作,由于继承,我们不需要这样做。

理想情况下,只有在所有情况下父类和子类的“ is-a ”关系均成立时才应使用继承,否则我们应该继续进行组合。

参考: Developer Recipes博客上的JCG合作伙伴 Pankaj Kumar的Java多重继承与Composition vs Inheritance 。

翻译自: https://www.javacodegeeks.com/2013/08/multiple-inheritance-in-java-and-composition-vs-inheritance.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367201.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql改date格式_mysql数据库修改添加Date格式列的方法

import java.sql.*;import java.text.DateFormat;//数据库的查询public class SelectTable {String dbDriver"com.mysql.jdbc.Driver";String dbUrl"jdbc:mysql://localhost:3306/sss";//根据实际情况变化String username"root";String password…

开源|蚂蚁金服开源AntV F2:一个专注于移动,开箱即用的可视

小蚂蚁说:AntV 是蚂蚁金服全新一代数据可视化解决方案,主要子产品包括 G2、G6、F2。此前我们已经相继发布过AntV的相关开源消息与版本迭代,包括《蚂蚁金服开源:数据驱动的高交互可视化图形语法G2》,《开源 | 蚂蚁金服开…

数学第一单元计算机思维导图,七年级下册数学第一单元思维导图图片

七年级下册数学第一单元思维导图图片_七年级数学下册思维导图第五章 相交线与平行线思维导图???邻补角? ???两条直线相交??对顶角???????相交线????????两条直线被第??三垂条直直线所截(三线八角)?????内同 同错旁 位角内 角角相 交 线 与 平 行…

DMA(Direct Memory Access)简介

什么是DMA(Direct Memory Access) DMA绕过CPU,在内存和外设之间开辟了一条 “隧道” ,直接控制内存与外设之间的操作,并完全由硬件控制。这样数据传送不经过cpu,不需要保护、恢复CPU现场等一系列操作&#…

Puppeteer入门初探

本文来自网易云社区作者:唐钊最近在看 node 爬虫相关的一些东西,我记得还是很久以前常用的 node 爬虫工具还是 superagengtcherrio,他们的思路是通过发起 http 请求然后截取 respone 的内容,但是随着前端mvvm等框架的盛行,现在更多…

访问量大如何增加服务器,服务器流量过大原因及解决方法

造成网站服务器流量过大的原因:1.网站规模较大(比如门户网站、网络商城等),即网站本身访问量需求大,查看网站的Page View值、Hits值、日流量都很高。2.网站页面设计不合理,页面中包含大图片或音频、视频文件等文件,导致…

关于VUE项目地图开发中大量点标记绘制一些总结

问题说明 在地图开发中,当地图中绘制大量的标记点后,无论是拖动或者缩放,都会感觉到明显的卡顿现象。(一般超过800个点后就比较明显了).在平时的工作业务中,由于公司的实时监控页面需要展现5000-20000车辆…

如何使用Java 5 Executor框架创建线程池

Java 5以Executor框架的形式在Java中引入了线程池,它允许Java程序员将任务提交与任务执行分离。 如果要使用Java进行服务器端编程,则线程池是维护系统可伸缩性,鲁棒性和稳定性的重要概念。 对于那些不熟悉Java中的线程池或这里的线程池的概念…

fedora python3-mysql_fedora 安装python mysql

如果你服务器环境允许yum,安装MySQL-python模块就很简单了。如果直接安装不行,先安装MySQL-devel后正常运行yum install mysql-develyum install MySQL-python -y1yum install MySQL–python–y当然也可以源码安装该模块首先安装 setuptools,…

var let 区别

var a 5; var b 10;if (a 5) {let a 4; // if 块级作用域var b 1; // 函数级作用域console.log(a); // 4console.log(b); // 1 } console.log(a); // 5 console.log(b); // 1var声明的变量是函数级的或者是全局的,而let用于声明块级作用域。 在函数或程序顶层…

服务器如何返回429状态,Instagram远程服务器返回一个错误:(429)UNKNOWN STATUS CODE...

我正在使用我自己的instagram api开发一些新程序。 一切都与我工作正常,除了以下的用户脚本 我想按照我的用户ID 的名单,所以我用这个代码Instagram远程服务器返回一个错误:(429)UNKNOWN STATUS CODEforeach (var item in listBox1.Items){We…

使用NetBeans 7.4 beta提示进行更好的基于JUnit的单元测试

在上一篇文章中 ,我写了NetBeans 7.4 beta中提供的提示 ,这些提示提高了开发人员避免Java异常处理带来的讨厌的运行时问题的能力。 在本文中,我将研究如何使用NetBeans 7.4 beta提供的另外两个提示使单元测试在执行单元测试期间更加正确和清晰…

yum 和 rpm安装mysql彻底删除

1、yum方式安装的MySQL $ yum remove mysql mysql-server mysql-libs compat-mysql51 $ rm -rf /var/lib/mysq $ rm /etc/my.cnf 查看是否还有mysql软件: $ rpm -qa|grep mysql 如果存在的话,继续删除即可,删除方式:yum remove 【…

Web 开发中 Blob 与 FileAPI 使用简述

本文节选自 Awesome CheatSheet/DOM CheatSheet,主要是对 DOM 操作中常见的 Blob、File API 相关概念进行简要描述。 Web 开发中 Blob 与 FileAPI 使用简述 Blob 是 JavaScript 中的对象,表示不可变的类文件对象,里面可以存储大量的二进制编…

django1.5 连接mysql_django1.5.5使用mysql

pythn3.3下实现django1.5.5连接mysqldjango1.5.5发布有一段时间了,最大的亮点在于支持python3.在版本上跟进了python3的各种应用。而在数据库方面,django1.5.5的mysqldb却还停止不前。幸在有mysql自发布的connector python连接器对python3的支持。准备工…

Thread部分总结以及小例子

Thread总结:一直以来用thread比较多,一般会在同步以及ui中用到.以下对于经常用作为简单介绍.一 实现方法: 一种直接new thread,另外一种是实现Runnable接口,在创建thread的时候将Runnable直接丢…

服务器e系列和l的区别,i.e.和 e.g.的区别和使用方法

举例说明在很多文章中都有使用过,我想这个对大家应该并不陌生,但是大家知道ie和eg的区别吗,他们两个都是举例子的缩写词,但是他们之间的区别大家知道吗,今天我们就来介绍下这两个举例说明的缩写词到底有什么不一样。一…

django QuerySet对象转换成字典对象

今天做一个添加购物车的模块,在添加商品之前要先验证用户是否处于登录状态,前台用ajax异步发送请求,后台在接收到请求后从session中取出用户登录信息。 根据登录信息从用户表中查询出对应用户,再将状态码和用户信息返回到前台&…

通过基于JDBC的用户存储部署Identity Server

在这篇文章中,我将演示如何使用JDBC用户存储配置WSO2 Identity Server。 为了演示,我使用的是MySQL用户存储,但是相同的过程也适用于任何其他JDBC用户存储。 我的环境是 操作系统– Ubuntu 12.10 Java – 1.6 WSO2是4.5.0 设置MySQL数据库…

前端路由实现原理(history)

前端路由实现(history) 了解: HTML5 history新增了两个API:history.pushState和history.replaceState 两个api都接受三个参数 状态对象(state object):一个JavaScript对象,与用pushState()方法…