关于Java的多继承
文章目录
- 一、为什么Java不支持多重继承?
- 1.1 Java不支持多重继承
- 1.2 为什么Java不支持多重继承?
- 1.2.1 简单
- 1.2.2 很少使用
- 二、如何给女朋友解释为什么Java不支持多继承?
- 2.1 继承
- 2.2 多继承
- 2.3 Java不支持多继承
- 2.4 Java 8支持多继承
- 三、实现多重继承
- 3.1 接口
- 3.2 内部类
- 四、知乎帖子
- 4.1-RednaxelaFX
- 4.2-CharlieW
- 4.3-解牛
- 最近在学习NDK和JNI相关,讲到了C++多继承问题,想的Java是不支持多继承的,就顺带看看这些资料,做个记录。
- 做股票软件,画K线图的时候看源码,又发现了他们定义的接口
extends
了多个父类……
public interface IKLine extends ICandle, IMACD, IKDJ, IRSI, IVolume, IWR {}
一、为什么Java不支持多重继承?
原文链接: Javapapers
翻译: ImportNew.com - 唐小娟
James Gosling在1995年2月发表了一篇名为”Java概览”的Java白皮书,文章解释了Java不支持多重继承的原因。
Java去除了一些C++中很少用、而且被经常误解而错用的功能,如操作符的重载(operator overloading)(尽管Java仍旧保留方法的重载),多重继承(multiple inheritance),以及广泛的自动强迫同型(extensive automatic coercions)。
没有谁比James Gosling更有资格来谈论这个这个话题了。这篇文章将为你介绍他对于Java不支持多重继承的看法。
1.1 Java不支持多重继承
首先我们要搞清楚,Java是否支持多重继承。有的人会说,Java中因为一个类可以实现多个接口,所以支持多重继承。错!Java不支持多重继承。如果你不相信我说的,那么再回到上面,重新读读Java之父的原话吧。
对于Java通过接口来实现多重继承的这个错误观点是有的程序员胡编乱造的。接口相对于具体的类赋予了我们更多的灵活性。我们可以一个类实现多个接口。这是约定俗成的,我们可以用两个’框架蓝图’来创造一个类。
我们做的只是实现多重接口,这里我们不是扩展(继续)什么类,而是实现一个类,在其中添加一些属性和行为。这不是从父类中直接获得一些行为和属性。在这里,我再次强调,Java不支持多重继承。
多重继承是一个子类从多个父类中继承属性和方法。C++, Common Lisp是时下支持多重继承的流行语言。
1.2 为什么Java不支持多重继承?
现在我们清楚了Java不支持多重继承了,那么Java为什么要这么做呢?这是Java创造者们的决定,最好的理由是因为简单,以及我们很少会用到它。
1.2.1 简单
我想在这里分享一下James Gosling对于Java的定义。
Java: 一种简单的,面向对象的,分布式的,解释型的(译者注:Java既不是纯解释型也不是纯编译型的语言),健壮的,安全的,架构中立的,可移植的,高性能的,支持多线程的,动态语言。
看看定义的美妙之处吧。现代程序语言应该有这样的特性。我们看到,定义第一个特性是什么?是简单。
为了强化简单这个特点,这就是我们去除多重继承的原因。下面来看个例子,多重继承的菱形继承问题。
有两个类B和C继承自A。假设B和C都继承了A的方法并且进行了覆盖,编写了自己的实现。假设D通过多重继承继承了B和C,那么D应该继承B和C的重载方法,那么它应该继承哪个的呢?是B的还是C的呢?
C++中经常会掉入这个陷阱,虽然它也提出了替代的方法来解决这个问题。我们在Java中就不会出现这个问题。就算两个接口拥有同样的方法,实现的类只会有一个方法,这个方法由实现的类编写。动态的加载类会让多重继承的实现变得困难。
1.2.2 很少使用
我们使用Java已经很长时间了,我们有多少次因为缺少多重继承而面临困难呢?我个人的经验是一次都没有。因为多重继承很少有机会被用到,所以更安全的做法是去掉它而保持简单性。
就算是碰到需要多重继承的情景,我们也可以找到替代方法。
我的观点是,去掉对多重继承的支持不是Java的缺陷,对开发者来说是件好事。
二、如何给女朋友解释为什么Java不支持多继承?
2.1 继承
面向对象的编程语言有三个重要的基本特性:封装、继承和多态。而很多人认为继承是Java面向对象编程技术的一块基石。
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的属性和方法;或子类从父类继承方法,使得子类具有父类相同的行为。
Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。
加入,我们已经定义了一个Car类,这个Car中包含了轮胎、发动机、底盘、方向盘等属性,还具有行走、加油、开窗等行为。而如果我们想要定义一辆Bus,想要复用这些属性和行为,就可以通过继承来实现。通过使用继承,我们使得Bus类和Car类之间存在了一定的关系,而我们通常称呼Car是Bus的父类,Bus是Car的子类。在Java中,使用extends
关键字来实现继承。
如上面Car与Bus,当写继承语句时,class Bus extends Car{ }
其中Bus类是子类,Car类是父类。
2.2 多继承
上面我们提到的Bus和Car之间的关系其实是一种单继承,指的是一个类只继承自一个父类。在软件开发中,还有一种多继承(多重继承)的情况,顾名思义,就是一个类同时继承自多个父类。比如维基百科中关于多继承举了一个例子:
例如,可以创造一个“哺乳类动物”类别,拥有进食、繁殖等的功能;然后定义一个子类型“猫”,它可以从父类继承上述功能,不需重新编写程序,同时增加属于自己的新功能,例如“追赶老鼠”。
但是,"猫"还可以作为"宠物"的子类,拥有一些宠物独有的能力。
作为面向对象语言,C++是支持多重继承的。
但是,多年以来,多重继承一直都是一个敏感的话题,反对者指它增加了程序的复杂性与含糊性。
2.3 Java不支持多继承
很多人知道,Java是不支持多重继承的,这里要提一下,这里的继承特指的是使用extends
关键字的这种继承行为。
那么为什么Java不支持多重继承呢?
关于这个问题,Java的创始人James Gosling曾经回答过,他表示:
“Java之所以不支持一个类继承多个类,主要是因为在设计之初我们听取了来自C++和Objective-C登阵营的人的意见。因为多继承会产生很多歧义问题。”
Gosling老人家提到的歧义问题,其实是C++因为支持多继承之后带来的菱形继承问题。
假设我们有类B和类C,它们都继承了相同的类A。另外我们还有类D,类D通过多重继承机制继承了类B和类C。
这时候,因为D同时继承了B和C,并且B和C又同时继承了A,那么,D中就会因为多重继承,继承到两份来自A中的属性和方法。在使用D的时候,如果想要调用一个定义在A中的方法时,就会出现歧义。因为这样的继承关系的形状类似于菱形,因此这个问题被形象地称为菱形继承问题。
而C++为了解决菱形继承问题,又引入了虚继承 。
因为支持多继承,引入了菱形继承问题,又因为要解决菱形继承问题,引入了虚继承。而经过分析,人们发现我们其实真正想要使用多继承的情况并不多。
所以,在 Java 中,不允许“实现多继承”,即一个类不允许继承多个父类。但是 Java 允许“声明多继承”,即一个类可以实现多个接口,一个接口也可以继承多个父接口。由于接口只允许有方法声明而不允许有方法实现(Java 8之前),这就避免了 C++ 中多继承的歧义问题。
2.4 Java 8支持多继承
Java不支持多继承,但是是支持多实现的,也就是说,同一个类可以同时实现多个类。
我们知道,在Java 8以前,接口中是不能有方法的实现的。所以一个类同时实现多个接口的话,也不会出现C++中的歧义问题。因为所有方法都没有方法体,真正的实现还是在子类中的。
那么问题来了。
Java 8中支持了默认函数(default method ),即接口中可以定义一个有方法体的方法了。
public interface Pet {public default void eat(){System.out.println("Pet Is Eating");}
}
而又因为Java支持同时实现多个接口,这就相当于通过implements就可以从多个接口中继承到多个方法了,这不就是变相支持了多继承么。
那么,Java是怎么解决菱形继承问题的呢?我们再定义一个哺乳动物接口,也定义一个eat方法。
public interface Mammal {public default void eat(){System.out.println("Mammal Is Eating");}
}
然后定义一个Cat,让他分别实现两个接口:
public class Cat implements Pet,Mammal {}
这时候,编译期会报错:
error: class Cat inherits unrelated defaults for eat() from types Mammal and Pet
这时候,就要求Cat类中,必须重写eat()方法。
public class Cat implements Pet,Mammal {@Overridepublic void eat() {System.out.println("Cat Is Eating");}
}
所以可以看到,Java并没有帮我们解决多继承的歧义问题,而是把这个问题留给开发人员,通过重写方法的方式自己解决。
三、实现多重继承
3.1 接口
在介绍接口和抽象类的时候了解到子类只能继承一个父类,也就是说只能存在单一继承,但是却可以实现多个接口,这就为我们实现多重继承做了铺垫。
对于接口而已,有时候它所表现的不仅仅只是一个更纯粹的抽象类,接口是没有任何具体实现的,也就是说,没有任何与接口相关的存储,因此也就无法阻止多个接口的组合了。
interface CanFight {void fight();
}interface CanSwim {void swim();
}interface CanFly {void fly();
}public class ActionCharacter {public void fight(){}
}public class Hero extends ActionCharacter implements CanFight,CanFly,CanSwim{public void fly() {}public void swim() {}/*** 对于fight()方法,继承父类的,所以不需要显示声明*/
}
3.2 内部类
上面使用接口实现多重继承是一种比较可行和普遍的方式,在介绍内部类的时候谈到内部类使的多继承的实现变得更加完美了,同时也明确了如果父类为抽象类或者具体类,那么我就仅能通过内部类来实现多重继承了。如何利用内部类实现多重继承,请看下面实例:儿子是如何利用多重继承来继承父亲和母亲的优良基因。
首先是父亲Father和母亲Mother:
public class Father {public int strong(){return 9;}
}public class Mother {public int kind(){return 8;}
}
重头戏在这里,儿子类Son:
public class Son {/*** 内部类继承Father类*/class Father_1 extends Father{public int strong(){return super.strong() + 1;}}class Mother_1 extends Mother{public int kind(){return super.kind() - 2;}}public int getStrong(){return new Father_1().strong();}public int getKind(){return new Mother_1().kind();}
}
测试程序:
public class Test1 {public static void main(String[] args) {Son son = new Son();System.out.println("Son 的Strong:" + son.getStrong());System.out.println("Son 的kind:" + son.getKind());}}
----------------------------------------
Output:
Son 的Strong:10
Son 的kind:6
儿子继承了父亲,变得比父亲更加强壮,同时也继承了母亲,只不过温柔指数下降了。这里定义了两个内部类,他们分别继承父亲Father类、母亲类Mother类,且都可以非常自然地获取各自父类的行为,这是内部类一个重要的特性:内部类可以继承一个与外部类无关的类,保证了内部类的独立性,正是基于这一点,多重继承才会成为可能。
有关于更多接口和内部类的详情,请参考这里:
- 接口: Java提高篇-----抽象类与接口
- 内部类: Java提高篇----详解内部类
四、知乎帖子
最后发现一个大宝库——知乎,有相关的讨论:
Java 为什么不支持多继承?
- java为什么不支持多继承?
- 有了接口为什么还要abstract class?
4.1-RednaxelaFX
4.2-CharlieW
再来说说abstract class和interface的区别。
而多重继承不但会造成冲突,还让一个类变得不伦不类,看不出这个类的本质,所以java毅然舍弃掉了这个祸害。
4.3-解牛
Java支持接口的多继承,不支持类的多继承,Java不支持类的多继承的一个原因是避免实现方法的版本冲突,看下面例子:
class Person{public void say(){System.out.println("Person say。");}
}class People{public void say(){System.out.println("People say。");}
}
class Student extends Person,People{public static void main(String[] args) {this.say();//不知道该执行哪个程序。}
}
interface People{public abstract void sayHello();public abstract void work();
}abstract class Chinese implements People{@Overridepublic void sayHello(){System.out.println("Chinese people say hello in Chinese.");}public abstract void work();
}
class Student extends Chinese{@Overridepublic void work() {System.out.println("My work is study Confucianism.");}
}
class ChineseTeacher extends Chinese{@Overridepublic void work() {System.out.println("My work is teach Chinese and Confucianism.");}
}
class Test{public static void main(String[] args) {People student = new Student();People chineseTeacher = new ChineseTeacher();student.sayHello();//Chinese people say hello in Chinese.chineseTeacher.sayHello();//Chinese people say hello in Chinese.student.work();//My work is study Confucianism.chineseTeacher.work();//My work is teach Chinese and Confucianism.}
}
关于Java的多继承相关推荐
- java 的继承_关于java中的继承
我们都知道Java中的继承是复用代码.扩展子类的一种方式,继承使得Java中重复的代码能够被提取出来供子类共用,对于Java程序的性能以及修改和扩展有很大的意义,所以这是一个非常重要的知识点. 那么对 ...
- 零基础Java学习之继承
继承 继承的概述 继承的理解 继承的好处 继承的格式 继承的特点一:成员变量 私有化(private) 成员变量不重名 成员变量重名 继承的特点二:成员方法 成员方法不重名 成员方法重名--重写(Ov ...
- Java异常以及继承的一些问题
Java异常以及继承的一些问题 参考文章: (1)Java异常以及继承的一些问题 (2)https://www.cnblogs.com/rookieJW/p/8059864.html 备忘一下.
- java容器类的继承结构
摘要: java容器类的继承结构 Java容器类库定义了两个不同概念的容器,Collection和Map Collection 一个独立元素的序列,这些元素都服从一条或多条规则.List必须按照插入的 ...
- java自学手记——继承
java面向对象三大特点封装.继承和多态.继承作为三大特点之一,主要是为了实现多态的,即多态的前提条件是继承.代码示例: 1 class Person{ 2 String name; 3 String ...
- Java基础:继承、多态、抽象、接口
第一讲 继承 一.继承概述 1.多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可. 2.通过extends关键字可以实现类与类的 ...
- java面向对象——包+继承+多态(一)
文章目录 包(package) 概念: 创建包 注意事项: 导入包中的类: 直接导入 import语句导入 注意事项: 静态导入(了解即可) 包的访问权限 常见的系统包 继承 继承的语法规则 注意要点 ...
- Java类的继承总结
本文主要是讲述Java类的继承,更多Java技术知识,请登陆疯狂软件教育官网.加疯狂软件官方微信号:fkitorg,免费赢大奖,有机会赢得iOS培训课程一套. 在 ...
- 腾讯架构师讲解Java接口的继承与抽象类
在实施接口中,我们利用interface语法,将interface从类定义中独立出来,构成一个主体.interface为类提供了接口规范. 在继承中,我们为了提高程序的可复用性,引入的继承机制.当时的 ...
- java中抽象类继承抽象类_用Java中的抽象类扩展抽象类
java中抽象类继承抽象类 示例问题 当我创建Java :: Geci抽象类AbstractFieldsGenerator和AbstractFilteredFieldsGenerator我遇到了一个不 ...
最新文章
- 【QT】Qtcreator的设计模式中将控件提升为自定义的控件
- 免费公开课 | 数据科学家,从入门到精进!【今晚福利】
- visual studio 2019配置OnnxRuntime+推理+vgg16
- 【Python】数据科学家提高效率的 40 个 Python 技巧
- 闫智宣的开发版_Android
- 【算法与数据结构专场】BitMap算法基本操作代码实现
- 微软确认5月2日召开新品发布会 8天后就是Build 2017大会
- 随感 -- 2013/08/16
- 机器学习速成课程 | 练习 | Google Development——编程练习:特征集
- 多线程导出excel高并发_怎么理解分布式、高并发、多线程
- SVN clean失败解决方法
- java 消息队列_消息队列-RabbitMQ在JAVA中的应用(1)
- 拼接播放地址_西安户外did拼接屏批发业务广泛_金伟达电子
- Jmeter中Websocket协议支持包的使用 (转)
- 矩阵求导及其链式法则
- Python期末考试编程题
- Day04_Manuals for Python@lisongye - list列表
- C++ 入门基础 取余数的应用价值
- vue启动报错98% after emitting CopyPlugin This dependency was not found:
- 汉高任命荣杰博士为大中华区总裁;沃尔玛中国2021届校招正式启动 | 美通企业日报...