Java8实战-总结39

news2024/11/15 11:38:47

Java8实战-总结39

  • 默认方法
    • 解决冲突的规则
      • 解决问题的三条规则
      • 选择提供了最具体实现的默认方法的接口
      • 冲突及如何显式地消除歧义
      • 菱形继承问题
    • 小结

默认方法

解决冲突的规则

Java语言中一个类只能继承一个父类,但是一个类可以实现多个接口。随着默认方法在Java 8中引入,有可能出现一个类继承了多个方法而它们使用的却是同样的函数签名。这种情况下,类会选择使用哪一个函数?在实际情况中,像这样的冲突可能极少发生,但是一旦发生这样的状况,必须要有一套规则来确定按照什么样的约定处理这些冲突。

Java编译器会解决这种潜在的冲突。例如“接下来的代码中,哪一个hello方法是被C类调用的”。接下来的例子主要用于说明容易出问题的场景,并不表示这些场景在实际开发过程中会经常发生。

	public interface A { 
		default void hello() { 
			System.out.println("Hello from A"); 
		} 
	} 
	
	public interface B extends A { 
		default void hello() { 
			System.out.println("Hello from B"); 
		} 
	} 
	
	public class C implements B, A { 
		public static void main(String... args) { 
			new C().hello(); 
		} 
	} 

菱形继承问题中一个类同时继承了具有相同函数签名的两个方法。到底该选择哪一个实现呢? Java 8提供了解决这个问题的方案。

解决问题的三条规则

如果一个类使用相同的函数签名从多个地方(比如另一个类或接口)继承了方法,通过三条规则可以进行判断。

  1. 类中的方法优先级最高。类或父类中声明的方法的优先级高于任何声明为默认方法的优先级。
  2. 如果无法依据第一条进行判断,那么子接口的优先级更高:函数签名相同时,优先选择拥有最具体实现的默认方法的接口,即如果B继承了A,那么B就比A更加具体。
  3. 如果还是无法判断,继承了多个接口的类必须通过显式覆盖和调用期望的方法,显式地选择使用哪一个默认方法的实现。

选择提供了最具体实现的默认方法的接口

回顾一下开头的例子,这个例子中C类同时实现了B接口和A接口,而这两个接口恰巧又都定义了名为hello的默认方法。另外,B继承自A。下图是这个场景的UML图。
在这里插入图片描述
按照规则2,编译器会使用选择的是提供了最具体实现的默认方法的接口。由于BA更具体,所以应该选择Bhello方法。所以,程序会打印输出“Hello from B”。

如果C像下面这样(如下图)继承自D,会发生什么情况:

public class D implements A{ } 

public class C extends D implements B, A { 
	public static void main(String... args) { 
		new C().hello(); 
	} 
}

在这里插入图片描述
依据规则1,类中声明的方法具有更高的优先级。D并未覆盖hello方法,可是它实现了接口A。所以它就拥有了接口A的默认方法。规则2说如果类或者父类没有对应的方法,那么就应该选择提供了最具体实现的接口中的方法。因此,编译器会在接口A和接口Bhello方法之间做选择。由于B更加具体,所以程序会再次打印输出“Hello from B”。

牢记这些判断的规则
在这个测验中继续复用之前的例子,唯一的不同在于D现在显式地覆盖了从A接口中
继承的hello方法。现在的输出会是什么呢?
public class D implements A { 
	void hello() { 
 		System.out.println("Hello from D"); 
	} 
} 
public class C extends D implements B, A { 
	public static void main(String... args) { 
		new C().hello(); 
 	} 
} 
答案:由于依据规则1,父类中声明的方法具有更高的优先级,所以程序会打印输出“Hello 
from D”。
注意,D的声明如下:
public abstract class D implements A { 
	public abstract void hello(); 
} 
这样的结果是,虽然在结构上,其他的地方已经声明了默认方法的实现,C还是必须提供
自己的hello方法。

冲突及如何显式地消除歧义

到目前为止,你看到的这些例子都能够应用前两条判断规则解决。让我们更进一步,假设B
不再继承A,(如下图所示):

public interface A { 
	void hello() { 
		System.out.println("Hello from A"); 
	} 
} 

public interface B { 
	void hello() { 
		System.out.println("Hello from B"); 
	} 
} 

public class C implements B, A { } 

这时规则2就无法进行判断了,因为从编译器的角度看没有哪一个接口的实现更加具体,两个都差不多。A接口和B接口的hello方法都是有效的选项。所以,Java编译器这时就会抛出一个编译错误,因为它无法判断哪一个方法更合适:Error: class C inherits unrelated defaults for hello()from types B and A.

在这里插入图片描述
冲突的解决
解决这种两个可能的有效方法之间的冲突,没有太多方案;只能显式地决定希望在C中使用哪一个方法。为了达到这个目的,可以覆盖类C中的hello方法,在它的方法体内显式地调用你希望调用的方法。Java 8中引入了一种新的语法X.super.m(…),其中X是希望调用的m方法所在的父接口。举例来说,如果希望C使用来自于B的默认方法,它的调用方式看起来就如下所示:

public class C implements B, A { 
	void hello() { 
		B.super.hello(); 
	} 
} 

几乎完全一样的函数签名这个测试中,假设接口AB的声明如下所示:

public interface A { 
	default Number getNumber() { 
		return 10; 
	} 
} 
public interface B { 
	default Integer getNumber(){ 
		return 42; 
	} 
} 

C的声明如下:

public class C implements B, A { 
	public static void main(String... args) { 
		System.out.println(new C().getNumber()); 
	} 
} 

这个程序的会打印输出什么呢?
答案:类C无法判断A或者B到底哪一个更加具体。这就是类C无法通过编译的原因。

菱形继承问题

最后一种场景,亦是C++里中最令人头痛的难题。

	public interface A { 
		default void hello() { 
			System.out.println("Hello from A"); 
		} 
	} 
	
	public interface B extends A { } 
	
	public interface C extends A { } 
	
	public class D implements B, C { 
		public static void main(String... args) { 
			new D().hello(); 
		} 
	} 

下图以UML图的方式描述了出现这种问题的场景。这种问题叫“菱形问题”,因为类的继承关系图形状像菱形。这种情况下类D中的默认方法到底继承自什么地方 ——源自B的默认方法,还是源自C的默认方法?实际上只有一个方法声明可以选择。只有A声明了一个默认方法。由于这个接口是D的父接口,代码会打印输出“Hello from A”。
在这里插入图片描述
现在,看看另一种情况,如果B中也提供了一个默认的hello方法,并且函数签名跟A中的方法也完全一致,这时会发生什么情况呢?根据规则2,编译器会选择提供了更具体实现的接口中的方法。由于BA更加具体,所以编译器会选择B中声明的默认方法。如果BC都使用相同的函数签名声明了hello方法,就会出现冲突,正如我们之前所介绍的,需要显式地指定使用哪个方法。

如果在C接口中添加一个抽象的hello方法(这次添加的不是一个默认方法),会发生什么情况呢?

public interface C extends A { 
	void hello(); 
} 

这个新添加到C接口中的抽象方法hello比由接口A继承而来的hello方法拥有更高的优先级,因为C接口更加具体。因此,类D现在需要为hello显式地添加实现,否则该程序无法通过编译。

C++语言中的菱形问题
C++语言中的菱形问题要复杂得多。首先,C++允许类的多继承。默认情况下,如果类D
继承了类B和类C,而类B和类C又都继承自类A,类D实际直接访问的是B对象和C对象的副本。
最后的结果是,要使用A中的方法必须显式地声明:这些方法来自于B接口,还是来自于C接口。
此外,类也有状态,所以修改B的成员变量不会在C对象的副本中反映出来。

如果一个类的默认方法使用相同的函数签名继承自多个接口,解决冲突的机制其实相当简单。只需要遵守下面这三条准则就能解决所有可能的冲突。

  • 首先,类或父类中显式声明的方法,其优先级高于所有的默认方法。
  • 如果用第一条无法判断,方法签名又没有区别,那么选择提供最具体实现的默认方法的接口。
  • 最后,如果冲突依旧无法解决,就只能在你的类中覆盖该默认方法,显式地指定在类中使用哪一个接口中的方法。

小结

  • Java 8中的接口可以通过默认方法和静态方法提供方法的代码实现。
  • 默认方法的开头以关键字default修饰,方法体与常规的类方法相同。
  • 向发布的接口添加抽象方法不是源码兼容的。
  • 默认方法的出现能帮助库的设计者以后向兼容的方式演进API
  • 默认方法可以用于创建可选方法和行为的多继承。
  • 有办法解决由于一个类从多个接口中继承了拥有相同函数签名的方法而导致的冲突。
  • 类或者父类中声明的方法的优先级高于任何默认方法。如果前一条无法解决冲突,那就选择同函数签名的方法中实现得最具体的那个接口的方法。
  • 两个默认方法都同样具体时,你需要在类中覆盖该方法,显式地选择使用哪个接口中提供的默认方法。

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

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

相关文章

核货宝:服装店收银系统如何选择?收银系统选购指南!

对于各行各业而言,收银系统都是必备的工具。特别是对于像服装店这样的零售门店来说,选择一套适合的收银系统尤为重要。在选择收银系统时,有一些关键的技巧需要注意,以达到软硬件合理搭配、节省开支的目的。下面将分享四个选购服装…

MAC版Gradle构建Spring5.X源码阅读环境

前言: 三年前鄙人有幸在现已几乎报废的Window的DELL中搭建过Spring源码环境,今天,Mac版的搭建,来了。 本篇文章环境搭建:Spring5.2.1 Gradle5.6.3-all jdk8 IDEA2022.3版本 文章目录 1、Spring源码下载2、Gradle下载…

设计模式 - 行为型模式:策略模式(概述 | 案例实现 | 优缺点 | 使用场景)

目录 一、行为型模式 1.1、策略模式 1.1.1、概论 1.1.2、案例实现 1.1.3、优缺点 1.1.4、使用场景 一、行为型模式 1.1、策略模式 1.1.1、概论 策略模式设计的每一个算法都封装了起来,使他们可以相互替换,通过一个对象委派不同的算法给相应的客户…

Dubbo 环境隔离

通过标签实现流量隔离环境(灰度、多套开发环境等) 无论是在日常开发测试环境,还是在预发生产环境,我们经常都会遇到流量隔离环境的需求。 在日常开发中,为了避免开发测试过程中互相干扰,我们有搭建多套独…

基于AlexNet深度学习网络的智能垃圾分类系统matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、基于AlexNet深度学习网络的智能垃圾分类系统概述 4.2、基于AlexNet深度学习网络的智能垃圾分类系统主要原理 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab20…

MySQ 学习笔记

1.MySQL(老版)基础 开启MySQL服务: net start mysql mysql为安装时的名称 关闭MySQL服务: net stop mysql 注: 需管理员模式下运行Dos命令 . 打开服务窗口命令 services.msc 登录MySQL服务: mysql [-h localhost -P 3306] -u root -p****** Navicat常用快捷键 键动作CTRLG设…

C语言——文件操作_学习笔记

一、引言——为什么使用文件 如果没有文件,我们写的程序的数据是存储在电脑的内存中,如果程序退出,内存回收,数据就丢失了,等再次运行程序,是看不到上次程序的数据的,如果要将数据进行持久化的…

仅用61行代码,你也能从零训练大模型

本文并非基于微调训练模型,而是从头开始训练出一个全新的大语言模型的硬核教程。看完本篇,你将了解训练出一个大模型的环境准备、数据准备,生成分词,模型训练、测试模型等环节分别需要做什么。AI 小白友好~文中代码可以直接实操运…

C++ 类和对象篇(三) 空类和6个默认成员函数

目录 一、空类 1. 是什么? 2. 空类中的成员 3. 空类的大小 二、6个默认成员函数 三、 构造函数 1. 构造函数是什么? 2. 为什么C要引入构造函数? 四、析构函数 1. 析构函数是什么? 2. 为什么要有析构函数? 五、拷贝构造…

硬件信号协议UART是干啥的?

UART协议是电子传输中一种常见的信号协议,通常只需要三根信号线就可以完成数据的收发,分别是TX,RX,GND。 UART(universal asynchronous receiver transmitter),是通用异步收发器,因为是异步,所以…

312.戳气球

将戳气球转换到添加气球&#xff0c;记忆搜索slove(i,j)&#xff1a;在开区间(i,j)全部填满气球得到的最多硬币数&#xff0c;两端val[i]、val[j] class Solution { public:vector<vector<int>> ans;vector<int> val;int slove(int left,int right){if(left&…

分布式存储系统如何应对SSD硬盘UNC坏块可靠性问题?

Uncorrectable Bit Errors&#xff08;UNC&#xff09;&#xff0c;也有时候叫做Media Error&#xff08;介质错误&#xff09;是指在闪存设备中出现的无法修复的位错误。Media Error的产生意味着即使硬盘自身已经用尽了所有容错手段&#xff08;如LDPC解码&#xff0c;retry等…

75.颜色分类

原地排序&#xff1a;空间复杂度为1 class Solution { public:void sortColors(vector<int>& nums) {if(0){//法一&#xff1a;单指针两个遍历int nnums.size();int ptr0;for(int i0;i<n;i){if(nums[i]0){swap(nums[i],nums[ptr]);ptr;}}for(int iptr;i<n;i){…

win11安装双系统Ubuntu的坎坷记录

之前一直装的都是在一个硬盘中&#xff0c;这是是两块盘。 我的电脑是惠普暗影精灵8Pro 一 安装前的准备工作 1.1 记得先关闭&#xff0c;Bitlocker 输入wins&#xff0c;搜索框输入&#xff1a;设备加密设置 1.2 BIOS设置 &#xff08;惠普这电脑是开机时按 F10&#xff0…

断点测试怎么做,一文教你用Charles 工具做好接口测试!

在测试工作过程中&#xff0c;我们经常会在程序的某一行或者某一环节设置断点&#xff0c;在程序请求的过程中&#xff0c;修改断点处的参数、请求或者响应&#xff0c;这就是所谓的断点测试。这类断点测试主要用于接口测试。 断点测试可以通过查看接口返回数据可以方便定位是前…

015-衍生版本开发

衍生版本开发 文章目录 衍生版本开发项目介绍衍生版本开发波形分析设置CPU 亲缘性 总结 关键字&#xff1a; Qt、 Qml、 关键字3、 关键字4、 关键字5 项目介绍 欢迎来到我们的 QML & C 项目&#xff01;这个项目结合了 QML&#xff08;Qt Meta-Object Language&#…

【Linux】基本指令-入门级文件操作(一)

目录 前言 ⭕linux的树状文件结构 ⭕绝对路径和相对路径 ⭕当前路径和上级路径 ⭕隐藏文件 基本指令&#xff08;重点&#xff09; 1 pwd 指令 2 mkdir 指令 3 touch 指令 4 ls 指令 4.1 ls只加选项不加文件/目录名&#xff0c;默认查看当前目录下的文件 4.1.1 ls -a…

360 G800行车记录仪,不使用降压线如何开机,8芯插头的定义。

G800记录仪的插头是这样的&#xff0c;图中标出了线的颜色。其中红色为常电V&#xff0c;黑色为GND负极&#xff0c;黄色为ACC受车是否启动控制。 这个记录仪原装的电源线没有降压功能&#xff0c;所以这里的V是12V。 记录仪内部有电源板&#xff0c;负责将12V降压为5V。 如果…

CSS盒子定位的扩张

定位的扩展 绝对定位&#xff08;固定定位&#xff09;会完全压住盒子 浮动元素不会压住下面标准流的文字&#xff0c;而绝对定位或固定位会压住下面标准流的所有内容 如果一个盒子既有向左又有向右&#xff0c;则执行左&#xff0c;同理执行上 显示隐藏 display: none&…

Tasmota系统之MQTT配置和使用篇

Tasmota系统之MQTT配置和使用篇 &#x1f6a9;相关篇《ESP32/ESP8266在线刷写Sonoff Tasmota固件以及配置简要》&#x1f4cc;《Tasmota系统之外设配置》&#x1f4cd;Tasmota官方对MQTT功能使用介绍&#xff1a;https://tasmota.github.io/docs/MQTT/ &#x1f6e0;MQTT本地化…