jdk新特性 02 .接口增强和函数式接口,方法引用

news2025/1/31 3:08:54

1.JDK8中接口的新增
在JDK8中针对接口有做增强,在JDK8之前

interface 接口名{
静态常量;
抽象方法;
}

JDK8之后对接口做了增加,接口中可以有默认方法和静态方法

interface 接口名{
静态常量;
抽象方法;
默认方法;
静态方法;
}

2.默认方法

2.1 为什么要增加默认方法
在JDK8以前接口中只能有抽象方法和静态常量,会存在以下的问题:
如果接口中新增抽象方法,那么实现类都必须要抽象这个抽象方法,非常不利于接口的扩展的
举个例子:
接口

interface A{
void test1();
// 接口中新增抽象方法,所有实现类都需要重写这个方法,不利于接口的扩展
void test2();
}

两个接口的实现类

class B implements A{
	@Override
	public void test1() {
	}
	@Override
	public void test2() {
	}
}
class C implements A{
	@Override
	public void test1() {
	}
	@Override
	public void test2() {
	}
}

主启动类:

package com.bobo.jdk.inter;
public class Demo01Interface {
	public static void main(String[] args) {
		A a = new B();
		A c = new C();
	}
}


2.2 接口默认方法的格式

接口中默认方法的语法格式是

interface 接口名{
修饰符 default 返回值类型 方法名{
方法体;
}
}

接口的第三个方法是默认方法:

interface A{
void test1();
// 接口中新增抽象方法,所有实现类都需要重写这个方法,不利于接口的扩展
void test2();
/**
* 接口中定义的默认方法
* @return
*/
public default String test3(){
	System.out.println("接口中的默认方法执行了...");
	return "hello";
}
}

在接口的实现类中默认方法可以不强制重写实现,也可以重写实现

class B implements A{
	@Override
	public void test1() {
	}
	@Override
	public void test2() {
	}
	@Override
	public String test3() {
		System.out.println("B 实现类中重写了默认方法...");
		return "ok ...";
	}
package com.bobo.jdk.inter;
public class Demo01Interface {
public static void main(String[] args) {
	A a = new B();
	a.test3();
	A c = new C();
	c.test3();
}
}


}

2.3接口中的默认方法有两种使用方式

  1. 实现类直接调用接口的默认方法
  2. 实现类重写接口的默认方法,再用这个

3.静态方法
JDK8中为接口新增了静态方法,作用也是为了接口的扩展
3.1 语法规则

接口的静态方法格式:

interface 接口名{
修饰符 static 返回值类型 方法名{
方法体;
}
}

举个例子:
接口:

interface A{
	void test1();
	// 接口中新增抽象方法,所有实现类都需要重写这个方法,不利于接口的扩展
	void test2();
	/**
	* 接口中定义的默认方法
	* @return
	*/
	public default String test3(){
		System.out.println("接口中的默认方法执行了...");
		return "hello";
	}
	/**
	* 接口中的静态方法
	* @return
	*/
	public static String test4(){
		System.out.println("接口中的静态方法....");
		return "Hello";
	}
}

接口实现类:

class B implements A{
	@Override
	public void test1() {
	}
	@Override
	public void test2() {
	}
	@Override//这个是接口实现类的默认方法的重载
	public String test3() {
	System.out.println("B 实现类中重写了默认方法...");
	return "ok ...";
	}
}
class C implements A{
	@Override
	public void test1() {
	}
	@Override
	public void test2() {
	}
}

3.2 静态方法的使用
接口中的静态方法在实现类中是不能被重写的,调用的话只能通过接口类型来实现: 接口名.静态方法名
();

在这里插入图片描述
4. 两者的区别介绍

  1. 默认方法通过实例调用,静态方法通过接口名调用
  2. 默认方法可以被继承,实现类可以直接调用接口默认方法,也可以重写接口默认方法
  3. 静态方法不能被继承,实现类不能重写接口的静态方法,只能使用接口名调用

函数式接口

1.1 函数式接口的由来
我们知道使用Lambda表达式的前提是需要有函数式接口,

而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型。

因此为了让我们使用Lambda表达式更加的方法,在JDK中提供了大量常用的函数式接口。
举个例子:

package com.bobo.jdk.fun;
public class Demo01Fun {
	public static void main(String[] args) {
		fun1((arr)->{//此处的lambda表达式参数是arr数组
			int sum = 0 ;
			for (int i : arr) {
				sum += i;
			}
			return sum;
		});
	}
	public static void fun1(Operator operator){
		int[] arr = {1,2,3,4};
		int sum = operator.getSum(arr);
		System.out.println("sum = " + sum);
	}
}
/**
* 函数式接口
*/
@FunctionalInterface
interface Operator{
	int getSum(int[] arr);
}

1.2@FunctionalInterface注解(带有这个注解的接口是函数式接口)

/**
* @FunctionalInterface
* 这是一个标志注解,被该注解修饰的接口只能声明一个抽象方法
*/
@FunctionalInterface
public interface UserService {
void show();
}

2 函数式接口介绍
在JDK中帮我们提供的有函数式接口,主要是在 java.util.function 包中。

2.1 Supplier

无参有返回值的接口,对于的Lambda表达式需要提供一个返回数据的类型。

Supplier接口:

@FunctionalInterface
public interface Supplier<T> {
	/**
	* Gets a result.
	*
	* @return a result
	*/
	T get();//这个是lambda关注
}

使用:

/**
* Supplier 函数式接口的使用
*/
public class SupplierTest {
	public static void main(String[] args) {
		fun1(()->{//lambda表达式
			int arr[] = {22,33,55,66,44,99,10};
			// 计算出数组中的最大值
			Arrays.sort(arr);
			return arr[arr.length-1];//返回值
		});
}
	private static void fun1(Supplier<Integer> supplier){//先要声明一个方法来用接口
		// get() 是一个无参的有返回值的 抽象方法
		Integer max = supplier.get();//用到了接口的抽象方法
		System.out.println("max = " + max);
}}

2.2 Consumer

有参无返回值得接口

前面介绍的Supplier接口是用来生产数据的,而Consumer接口是用来消费数据的,使用的时候需要指定一个泛型来定义参数类型
Consumer 接口:

@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);//接口的抽象方法,lambda关注的重点
}

使用:

public class ConsumerTest {
	public static void main(String[] args) {
		test(msg -> {//有参,参数是Hello World
			System.out.println(msg + "-> 转换为小写:" + msg.toLowerCase());
		});
	}
public static void test(Consumer<String> consumer){//声明一个方法,来用comsumer接口
	consumer.accept("Hello World");//在方法内用接口的抽象方法
	}
}

2.3 Function

有参有返回值的接口,

Function接口是根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。

Function接口:

@FunctionalInterface
public interface Function<T, R> {
	/**
	* Applies this function to the given argument.
	*
	* @param t the function argument
	* @return the function result
	*/
	R apply(T t);//接口的抽象方法,lambda关注的重点,有参数T t,有返回值R
}

运用:

public class FunctionTest {
	public static void main(String[] args) {
		test(msg ->{//lambda表达式,msg是test方法中字符串
			return Integer.parseInt(msg);
		});
	}
	public static void test(Function<String,Integer> function){//先声明一个方法,用到接口
		Integer apply = function.apply("666");//接口的抽象方法,接口中的字符串是前提条件,也就代表参数是字符串类型的
		System.out.println("apply = " + apply);
	}
}

2.4 Predicate
有参且返回值为Boolean的接口
Predicate接口:

@FunctionalInterface
public interface Predicate<T> {
	/**
	* Evaluates this predicate on the given argument.
	*
	* @param t the input argument
	* @return {@code true} if the input argument matches the predicate,
	* otherwise {@code false}
	*/
	boolean test(T t);//接口的抽象方法,lambda关注的重点,有参数T t,有返回值
}

运用:

public class PredicateTest {
	public static void main(String[] args) {
		test(msg -> {//msg是参数,值是后面的那个字符串HelloWorld,这个是test方法中
			return msg.length() > 3;
		},"HelloWorld");
	}
	private static void test(Predicate<String> predicate,String msg){//先声明一个方法,用到接口
		boolean b = predicate.test(msg);//接口的抽象方法
		System.out.println("b:" + b);
	}
}

方法引用
01 lambda表达式冗余
在使用Lambda表达式的时候,也会出现代码冗余的情况,比如:用Lambda表达式求一个数组的和

package com.bobo.jdk.funref;
import java.util.function.Consumer;
public class FunctionRefTest01 {
	public static void main(String[] args) {
		printMax(a->{
		// Lambda表达式中的代码和 getTotal中的代码冗余了
			int sum = 0;
			for (int i : a) {
				sum += i;
			}
			System.out.println("数组之和:" + sum);
		});
	}
	/**
	* 求数组中的所有元素的和
	* @param a
	*/
	public void getTotal(int a[]){
		int sum = 0;
		for (int i : a) {
			sum += i;
		}
		System.out.println("数组之和:" + sum);
	}
	private static void printMax(Consumer<int[]> consumer){
		int[] a= {10,20,30,40,50,60};
		consumer.accept(a);
	}
}

0.2 解决方案
因为在Lambda表达式中要执行的代码和我们另一个方法中的代码是一样的,这时就没有必要重写一份逻辑了,这时我们就可以“引用”重复代码

package com.bobo.jdk.funref;
import java.util.function.Consumer;
public class FunctionRefTest02 {
	public static void main(String[] args) {
	// :: 方法引用 也是JDK8中的新的语法
	printMax(FunctionRefTest02::getTotal);
	}
	/**
	* 求数组中的所有元素的和
	* @param a
	*/
	public static void getTotal(int a[]){
		int sum = 0;
		for (int i : a) {
			sum += i;
		}
		System.out.println("数组之和:" + sum);
	}
	private static void printMax(Consumer<int[]> consumer){
		int[] a= {10,20,30,40,50,60};
		consumer.accept(a);//抽象方法运用,有参数
	}
}

0 3. 方法引用的格式

符号表示: ::

符号说明:双冒号为方法引用运算符,而它所在的表达式被称为 方法引用

应用场景:如果Lambda表达式所要实现的方案,已经有其他方法存在相同的方案,那么则可以使用方法引用。

常见的引用方式:
方法引用在JDK8中使用是相当灵活的,有以下几种形式:

  1. instanceName::methodName 对象::方法名
  2. ClassName::staticMethodName 类名::静态方法
  3. ClassName::methodName 类名::普通方法
  4. ClassName::new 类名::new 调用的构造器
  5. TypeName[]::new String[]::new 调用数组的构造器

3.1 对象名::方法名
这是最常见的一种用法。如果一个类中的已经存在了一个成员方法,则可以通过对象名引用成员方法

public static void main(String[] args) {
	Date now = new Date();
	Supplier<Long> supplier = ()->{return now.getTime();};//lambda表达式
	System.out.println(supplier.get());//这个接口的实例对象的get方法已经重写了
	// 然后我们通过 方法引用 的方式来处理
	Supplier<Long> supplier1 = now::getTime;
	System.out.println(supplier1.get());
}

方法引用的注意事项:

  1. 被引用的方法,参数要和接口中的抽象方法的参数一样
  2. 当接口抽象方法有返回值时,被引用的方法也必须有返回值

3.2 类名::静态方法名
也是比较常用的方式:

public class FunctionRefTest04 {
	public static void main(String[] args) {
		Supplier<Long> supplier1 = ()->{
		return System.currentTimeMillis();
	};
	System.out.println(supplier1.get());
	// 通过 方法引用 来实现
	Supplier<Long> supplier2 = System::currentTimeMillis;
	System.out.println(supplier2.get());
	}
}

3.3 类名::引用实例方法
Java面向对象中,类名只能调用静态方法,类名引用实例方法是用前提的,实际上是拿第一个参数作为方法的调用者

package com.bobo.jdk.funref;
import java.util.Date;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
public class FunctionRefTest05 {
	public static void main(String[] args) {
		Function<String,Integer> function = (s)->{//这里是重写方法,参数是字符串类型,但是值不知道
		return s.length();
		};
	System.out.println(function.apply("hello"));
	// 通过方法引用来实现
	Function<String,Integer> function1 = String::length;
	System.out.println(function1.apply("hahahaha"));
	BiFunction<String,Integer,String> function2 = String::substring;
	String msg = function2.apply("HelloWorld", 3);
	System.out.println(msg);
	}
}

3.4类名::构造器
由于构造器的名称和类名完全一致,所以构造器引用使用 ::new 的格式使用,

import java.util.Date;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
public class FunctionRefTest06 {
	public static void main(String[] args) {
		Supplier<Person> sup = ()->{return new Person();};
		System.out.println(sup.get());
		// 然后通过 方法引用来实现
		Supplier<Person> sup1 = Person::new;
		System.out.println(sup1.get());
		BiFunction<String,Integer,Person> function = Person::new;
		System.out.println(function.apply("张三",22));
	}
}

3.5 数组::构造器
数组是怎么构造出来的呢?

public static void main(String[] args) {
	Function<Integer,String[]> fun1 = (len)->{//这是重写接口的抽象方法
	return new String[len];
	};
	String[] a1 = fun1.apply(3);
	System.out.println("数组的长度是:" + a1.length);
	// 方法引用 的方式来调用数组的构造器
	Function<Integer,String[]> fun2 = String[]::new;
	String[] a2 = fun2.apply(5);
	System.out.println("数组的长度是:" + a2.length);
}

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

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

相关文章

OS 内核级线程

用户级线程是两个栈&#xff0c;核心级线程是两套栈&#xff0c;用户栈和内核栈 用户级是并发&#xff08;同时触发、交替执行&#xff09;&#xff0c;这个是并行&#xff08;同时触发可以同时执行&#xff09; 进入内核的唯一方式是中断 根据TCB的切换&#xff0c;实现内核…

【经验贴】新手项目经理如何接手并管好项目?

最近有刷到这样一些求助帖&#xff1a;初入职场两三年的项目经理现在开始独立带项目&#xff0c;由于缺乏经验不知道从何下手&#xff0c;询问如何能快速接手并管好项目呢&#xff1f;这个话题也引起了大家的热议&#xff0c;今天就给大家分享一下一些实践经验。 1.刚拿到项目时…

如何做好项目进度管理?来看这几个要点!

8个项目管理工具模板、60个项目管理甘特图标模板、赠送30本项目管理电子书https://download.csdn.net/download/XMWS_IT/19886618?spm1001.2014.3001.5503 项目进度管理是指在项目实施过程中&#xff0c;对各阶段的进展程度和项目最终完成的期限所进行的管理。其目的是保证项目…

clickhouse-压测

一、数据集准备 数据集可以使用官网数据集&#xff0c;也可以用ssb-dbgen来准备 1.准备数据 这里最后生成表的数据行数为60亿行&#xff0c;数据量为300G左右 git clone https://github.com/vadimtk/ssb-dbgen.git cd ssb-dbgen/ make1.1 生成数据 # -s 指生成多少G的数据…

Linux C 多进程编程(面试考点)

嵌入式开发为什么要移植操作系统&#xff1f; 1.减小软硬件的耦合度&#xff0c;提高软件的移植性 2. 操作系统提供很多库和工具&#xff08;QT Open CV&#xff09;&#xff0c;提高开发效率 3.操作系统提供多任务机制&#xff0c;______________________? (提高C…

Zenity 简介

什么使 Zenity Zenity 是一个开源的命令行工具&#xff0c;它提供了一种简单的方式来创建图形化的用户界面&#xff08;GUI&#xff09;对话框&#xff0c;以与用户进行交互。它基于 GTK 库&#xff0c;可以在 Linux 和其他 UNIX-like 系统上使用。 Zenity 可以通过命令行或脚…

最新政策丨政务服务电子文件归档和电子档案管理办法说了什么?

随着数字化时代的持续演进&#xff0c;我国政府部门正积极推动数字政府建设&#xff0c;以优化政务服务&#xff0c;提升办事效率。为了适应这一背景&#xff0c;国务院发布了《政务服务电子文件归档和电子档案管理办法》&#xff0c;旨在规范电子档案管理&#xff0c;加强政务…

为什么使用消息队列?消息队列能够做什么?消息队列有哪些?怎么选择?

❤ 作者主页&#xff1a;李奕赫揍小邰的博客 ❀ 个人介绍&#xff1a;大家好&#xff0c;我是李奕赫&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 记得点赞、收藏、评论⭐️⭐️⭐️ &#x1f4e3; 认真学习!!!&#x1f389;&#x1f389; 文章目录 为什么使用消…

msvcp110.dll下载安装方法分享,教你怎么快速的修复msvcp110.dll文件

当你的电脑出现msvcp110.dll文件缺失时&#xff0c;这时候不要慌张&#xff0c;其实要解决这个问题很简单&#xff0c;我们只要重新下载安装msvcp110.dll文件就可以了&#xff0c;今天主要是来给大家讲解一下这方面的信息&#xff0c;教大家如何下载安装msvcp110.dll。 一.了解…

MPDIoU:有效和准确的边界框回归的损失

文章目录 摘要1、简介2、相关工作2.1、目标检测和实例分割2.2. 场景文本识别2.3、边界框回归的损失函数 3、点距最小的并集交点4、实验结果4.1、 实验设置4.2、数据集4.3、 评估协议4.4、 目标检测的实验结果4.5、 字符级场景文本识别的实验结果4.6、 实例分割的实验结果 5、 结…

Anomalib:异常检测的深度学习库 -- 应用Anomalib训练自己的图片

文章目录 资料汇总 Github链接&#xff1a;https://github.com/openvinotoolkit/anomalib/blob/main/README.md 论文链接&#xff1a;https://arxiv.org/pdf/2202.08341v1.pdf 其他参考资料&#xff1a;https://paperswithcode.com/paper/efficientad-accurate-visual-anomaly-…

突破限制,创造佳绩!国内工作流厂商助您腾飞!

随着业务量的激增&#xff0c;很多企业单位都想在办公领域更上一层楼&#xff0c;实现飞跃式地腾飞。采用什么样的软件设备能助力企业降本增质&#xff1f;国内工作流厂商流辰信息作为研发低代码技术平台的服务商&#xff0c;一直深知行业形式和发展动态&#xff0c;将全力以赴…

前端开发工具: VSCode

VSCode 安装使用教程&#xff08;图文版&#xff09; | arry老师的博客-艾编程 1. 下载 在官方网站&#xff1a;https://code.visualstudio.com/ 下载最新版本的 VSCode 即可 2. VSCode 常见插件安装 所有插件安装后,需要重启一下才生效 2.1 简体中文语言包 2.2 编辑器主…

四信重磅推出5G RedCap AIoT摄像机 RedCap轻量级5G终端新品首发!

6月6日&#xff0c;四信受邀出席移动物联网高质量发展论坛&#xff0c;并在移动物联网新产品发布环节隆重推出5G RedCap AIoT摄像机&#xff0c;再次抓紧需求先机&#xff0c;为行业用户创造无限可能&#xff01; 两大应用场景 助推RedCap走深向实 火遍全网络的RedCap应用场景可…

Git gui教程---第七篇 Git gui的使用 返回上一次提交

1&#xff0e; 查看历史&#xff0c;打开gitk程序 2&#xff0e; 选中需要返回的版本&#xff0c;右键&#xff0c;然后点击Rest master branch to here 3.出现弹窗 每个选项我们都试一下&#xff0c;从Hard开始 返回的选项 HardMixedSoft Hard 会丢失所有的修改【此处的…

List 去重两种方式:stream(需要JDK1.8及以上)、HashSet

1、使用Stream 方法 使用JDK1.8及以上 /*** Java合并两个List并去掉重复项的几种做法* param args*/public static void main(String[] args) {String[] str1 {"1", "2", "3", "4", "5", "6"};List<String&…

Protobuf 原理大揭秘

一、定义 Google推出的一种 结构化数据 的数据存储格式&#xff08;类似于 XML、Json &#xff09;。 多个版本的源码地址 https://github.com/protocolbuffers/protobuf/ 1、为什么选择它 优点&#xff1a; 效率高&#xff1a;Protobuf 以二进制格式存储数据&#xff0c;比…

汇编语言调试工具:DosBox及debug安装配置使用教程

前言 学习汇编语言时&#xff0c;需要进入dos模式并使用debug工具调试。但是64位win10系统没有自带这些工具。因此&#xff0c;需要额外安装DosBox和debug.exe两个软件。本文介绍如何下载、安装、配置这两个工具软件。 1、DosBox下载 简介 DOSBox 是一个 DOS 模拟程序&#xf…

SMOKE多模式排放清单处理技术及EDGAR/MEIC清单制作与VOCs排放量核算

1、掌握大气污染源排放清单不确定性来源及定量分析方法&#xff1b; 2、以VOCs排放为例&#xff0c;掌握排放源核算及组分清单建立方法; 3、掌握基于SMOKE模型的大气污染源排放清单处理技术方法&#xff1b; 4、掌握基于SMOKE的多模式排放清单输入制备方法&#xff1b;5、通过…

时序预测 | Matlab实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元时间序列预测

时序预测 | Matlab实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元时间序列预测 目录 时序预测 | Matlab实现SO-CNN-BiGRU蛇群算法优化卷积双向门控循环单元时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 时序预测 | Matlab实现SO-CNN-BiGRU蛇群算法优化…