JDK8特性——接口增强函数式接口Optional方法引用

文章目录

  • 接口增强
    • 默认方法
    • 静态方法
  • 函数式接口
    • Supplier
    • Consumer
    • Function
    • Predicate
  • Optional 类
    • 以前对null 的处理
    • Optional的基本使用
    • Optional的常用方法
  • 方法引用
    • 方法引用的格式
    • 对象名::方法名
    • 类名::静态方法名
    • 类名::引用实例方法
    • 类名::构造器
    • 数组::构造器

接口增强

在JDK8之前,接口的结构如下:

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

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

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

默认方法

为什么要增加默认方法?

在JDK8以前接口中只能有抽象方法和静态常量,会存在以下的问题:

如果接口中新增抽象方法,那么实现类都必须要实现这个抽象方法,非常不利于接口的扩展的

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

如果接口A新增了test3()方法,接口B、C都需要实现,也不符合开闭原则。

默认方法的格式

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

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

默认的方法,实现类可实现可不实现,当然也可以通过实例调用默认方式。

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

静态方法

静态方法作用也是为了接口的扩展

静态方法的格式

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";}
}

在这里插入图片描述

函数式接口

使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名、抽象方法名,只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方法,JDK中提供了大量常用的函数式接口,主要是在 java.util.function 包中。

Supplier

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

@FunctionalInterface
public interface Supplier<T> {/*** Gets a result.** @return a result*/T get();
}

Supplier 函数式接口的使用

/*** */
public class SupplierTest {public static void main(String[] args) {fun1(()->{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);}
}

Consumer

Consumer是有参无返回值得接口,Supplier接口是用来生产数据的,而Consumer接口是用来消费数据的,使用的时候需要指定一个泛型来定义参数类型

@FunctionalInterface
public interface Consumer<T> {/*** Performs this operation on the given argument.** @param t the input argument*/void accept(T t);
}

使用:将输入的数据统一转换为小写输出

public class ConsumerTest {public static void main(String[] args) {test(msg -> {System.out.println(msg + "-> 转换为小写:" + msg.toLowerCase());});}public static void test(Consumer<String> consumer){consumer.accept("Hello World");}
}

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);
}

使用:传递进入一个字符串返回一个数字

public class FunctionTest {public static void main(String[] args) {test(msg ->{return Integer.parseInt(msg);});}public static void test(Function<String,Integer> function){Integer apply = function.apply("666");System.out.println("apply = " + apply);}
}

Predicate

Predicate是一个有参且返回值为Boolean的接口

@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);
}

使用:

public class PredicateTest {public static void main(String[] args) {test(msg -> {return msg.length() > 3;});}private static void test(Predicate<String> predicate){boolean b = predicate.test("HelloWorld");System.out.println("b:" + b);}
}

在Predicate中的默认方法提供了逻辑关系操作 and、or、negate、isEquals方法

package com.bobo.jdk.fun;import java.util.function.Predicate;public class PredicateDefaultTest {public static void main(String[] args) {test(msg1 -> {return msg1.contains("H");},msg2 -> {return msg2.contains("W");});}private static void test(Predicate<String> p1,Predicate<String> p2){// p1 包含H 同时 p2 包含Wboolean bb1 = p1.and(p2).test("Hello");// p1 包含H 或者 p2 包含Wboolean bb2 = p1.or(p2).test("Hello");// p1 不包含Hboolean bb3 = p1.negate().test("Hello");System.out.println(bb1); // FALSESystem.out.println(bb2); // TRUESystem.out.println(bb3); // FALSE}
}

Optional 类

Java的Optional类是一个用于解决null安全问题的工具类。在Java 8中引入了这个类,它提供了一种更优雅、更安全的方式来处理可能为null的值。

以前对null 的处理

    @Testpublic void test01(){String userName = null;if(userName != null){System.out.println("字符串的长度:" + userName.length());}else{System.out.println("字符串为空");}}

Optional的基本使用

Optional对象的创建方式

    /*** Optional对象的创建方式*/@Testpublic void test02(){// 第一种方式 通过of方法  of方法是不支持null的Optional<String> op1 = Optional.of("zhangsan");//Optional<Object> op2 = Optional.of(null);// 第二种方式通过 ofNullable方法 支持nullOptional<String> op3 = Optional.ofNullable("lisi");Optional<Object> op4 = Optional.ofNullable(null);// 第三种方式 通过empty方法直接创建一个空的Optional对象Optional<Object> op5 = Optional.empty();}

Optional的常用方法

/*** Optional中的常用方法介绍*   get(): 如果Optional有值则返回,否则抛出NoSuchElementException异常*          get()通常和isPresent方法一块使用*   isPresent():判断是否包含值,包含值返回true,不包含值返回false*   orElse(T t):如果调用对象包含值,就返回该值,否则返回t*   orElseGet(Supplier s):如果调用对象包含值,就返回该值,否则返回 Lambda表达式的返回值* 	 ifPresent*/@Testpublic void test03(){Optional<String> op1 = Optional.of("zhangsan");Optional<String> op2 = Optional.empty();// 获取Optional中的值if(op1.isPresent()){String s1 = op1.get();System.out.println("用户名称:" +s1);}// isPresent()&get()简洁表达op1.ifPresent(s-> System.out.println("有值:" +s));if(op2.isPresent()){System.out.println(op2.get());}else{System.out.println("op2是一个空Optional对象");}String s3 = op1.orElse("李四");System.out.println(s3);String s5 = op2.orElseGet(()->{return "Hello";});System.out.println(s5);}

方法引用

为什么要用方法引用?

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

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);}
}

上述代码中在Lambda表达式中要执行的代码和getTotal方法中的代码是一样的,没有必要重写一份逻辑了,这时我们就可以“引用”重复代码

public class FunctionRefTest01 {public static void main(String[] args) {// :: 方法引用 也是JDK8中的新的语法printMax(FunctionRefTest02::getTotal);}

方法引用的格式

符号表示:::

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

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

方法引用的注意事项:

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

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

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

对象名::方法名

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

    public static void main(String[] args) {Date now = new Date();Supplier<Long> supplier = ()->{return now.getTime();};System.out.println(supplier.get());// 然后我们通过 方法引用 的方式来处理Supplier<Long> supplier1 = now::getTime;System.out.println(supplier1.get());}

类名::静态方法名

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());}
}

类名::引用实例方法

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

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"));}
}

类名::构造器

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

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));}
}

数组::构造器

    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);}

方法引用是对Lambda表达式符合特定情况下的一种缩写方式,它使得我们的Lambda表达式更加的精简,也可以理解为lambda表达式的缩写形式,不过要注意的是方法引用只能引用已经存在的方法。

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

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

相关文章

智能远程监考方案助力企业考试化繁为简

在音视频数字化之旅中&#xff0c;轻装上阵。 近年来&#xff0c;在数字化浪潮之下&#xff0c;远程考试频繁成为各领域热词&#xff0c;各企业也纷纷改革求新&#xff0c;将原本的企业内部考试转移到线上&#xff0c;从而获取更低廉的组考成本&#xff0c;更高的管理效率&…

深度学习基础之梯度下降

1. 引言 梯度下降是一种用于最小化&#xff08;或最大化&#xff09;损失函数的优化算法。它是机器学习和深度学习中的一个关键概念&#xff0c;通常用于调整学习算法中的参数。 梯度下降背后的核心思想是迭代调整参数以最小化损失函数。它的工作原理是计算损失函数相对于每个…

【LeetCode-中等题】18. 四数之和

文章目录 题目方法一&#xff1a;双指针&#xff08;定2动2&#xff09; 题目 方法一&#xff1a;双指针&#xff08;定2动2&#xff09; 这题可以参考【LeetCode-中等题】15. 三数之和 区别在于&#xff0c;三数之和只需要用一个for循环定住一个数&#xff0c;然后设置两个前…

数据结构与算法(C语言版)P4---顺序表、链表总结

顺序表和链表&#xff08;双向带头链表&#xff09;的区别 顺序表&#xff1a; 优点&#xff1a; 支持随机访问。需要随机访问结构支持算法可以很好的使用。cpu高速缓存利用率&#xff08;命中率&#xff09;更高。存储密度高 缺点&#xff1a; 头部中部插入删除时间效率低。…

合宙Air724UG LuatOS-Air LVGL API控件-窗口 (Window)

窗口 (Window) 分 享导出pdf 示例代码 win lvgl.win_create(lvgl.scr_act(), nil) lvgl.win_set_title(win, "Window title") -- close_btn lvgl.win_add_btn_right(win, "\xef\x80\x8d") -- --lvgl.obj_set_event_cb(cl…

典型数据结构-图,图的存储、基本操作和遍历

图 引自&#xff1a;《数据结构教程》。 概念 图可以使得元素之间的关系是 多对多。图中任意两个数据元素之间都可能存在连接关系。图作为一种数据结构&#xff0c;可以表达数据元素之间广泛存在着的更为复杂的关系。在众多应用之中&#xff0c;如电子线路分析、工程计划分析、…

Junit单元测试异常处理方法

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 Junit单元测试异常处理方法 前言案例准备一、类方法内处理异常二、测试方法中处理异常1.try/catch/finally 语句2.Test(expected)3.ExpectedException 前言 提示&#xff1a…

深度解析shell脚本的命令的原理之mv

mv 是 Unix 或 Linux 中的一个基本命令&#xff0c;用于移动或重命名文件和目录。以下是对这个命令的深度解析&#xff1a; 基本操作&#xff1a;mv 命令的基本操作是将一个或多个源文件或目录移动到一个目标文件或目录&#xff0c;或者重命名源文件或目录。这是通过改变文件系…

银河麒麟--国产操作系统-九五小庞

那么&#xff0c;我国国产操作系统现状到底如何呢&#xff1f; 自 1999 年徐冠华部长一语点破我们的产业软肋之后&#xff0c;国产操作系统起步于国家“七五”计划期间&#xff0c;目前国产操作系统均是基于Linux内核进行的二次开发&#xff0c;中国国产操作系统进入Linux元年…

CSS:隐藏移动端的滚动条的方式

目录 方式一&#xff1a;-webkit-scrollbar方式二&#xff1a;overflow方式三&#xff1a;clip-path方式四&#xff1a;mask 遮罩总结参考 移动端开发中&#xff0c;有一个横向滚动元素&#xff0c;产品告诉我不需要滚动条&#xff0c;我说这个简单&#xff0c;隐藏一下不就行了…

iText实战--在现有PDF上工作

6.1 使用PdfReader读取PDF 检索文档和页面信息 D:/data/iText/inAction/chapter03/image_direct.pdf Number of pages: 1 Size of page 1: [0.0,0.0,283.0,416.0] Rotation of page 1: 0 Page size with rotation of page 1: Rectangle: 283.0x416.0 (rot: 0 degrees) Is reb…

深度思考ES面经

1 推荐文章 2万字详解&#xff0c;吃透 Elasticsearch 2 什么是倒排索引&#xff0c;为什么这么叫&#xff1f; 倒排索引&#xff08;Inverted Index&#xff09;是一种为快速全文搜索而设计的数据结构。它被广泛应用于搜索引擎&#xff0c;其中 Elasticsearch&#xff08;简…

C# 扫描并读取图片中的文字(.NET Core)

本文介绍如何通过C# 程序来扫描并读取图片中的文字&#xff0c;这里以创建一个.Net Core程序为例。下面是具体步骤&#xff0c;供参考。 程序测试环境&#xff1a; Visual Studio版本要求不低于2017 图片扫描工具&#xff1a;Spire.OCR for .NET 图片格式&#xff1a;png&…

JSP ssm 网上求职管理系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 JSP ssm 网上求职管理系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采…

zookeeper最基础教程

文章目录 一、简介1、工作机制2、特点3、数据结构4、应用场景5、选举机制 二、软件安装1、单机版安装2、集群安装3、配置参数解读(zoo.cfg)4、ZK集群启动脚本 三、命令行操作1、语法2、使用3、节点相关4、监听器原理5、节点删除与查看 三、写数据流程 一、简介 1、工作机制 官…

SQL优化--排序优化(order by)

Using filesort : 通过表的索引或全表扫描&#xff0c;读取满足条件的数据行&#xff0c;然后在排序缓冲区sort buffer中完成排序操作&#xff0c;所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。 Using index : 通过有序索引顺序扫描直接返回有序数据&#xff0c…

1031. 两个非重叠子数组的最大和

1031. 两个非重叠子数组的最大和 原题链接&#xff1a;完成情况&#xff1a;解题思路&#xff1a;参考代码&#xff1a; 原题链接&#xff1a; 1031. 两个非重叠子数组的最大和 https://leetcode.cn/problems/maximum-sum-of-two-non-overlapping-subarrays/description/ 完…

【C语言】指针的进阶(一)

目录 前言 1. 字符指针 2. 指针数组 3. 数组指针 3.1 数组指针的定义 3.2 &数组名VS数组名 3.3 数组指针的使用 4. 数组参数、指针参数 4.1 一维数组传参 4.2 二维数组传参 4.3 一级指针传参 4.4 二级指针传参 5. 函数指针 前言 指针在C语言中可谓是有着举足轻重的…

Arm发布 Neoverse V2 和 E2:下一代 Arm 服务器 CPU 内核

9月14日&#xff0c;Arm发布了新的处理器内核&#xff1a;V2和E2&#xff0c;在官网已经可以看到相关的TRM 手册了。。 四年前&#xff0c;Arm发布了Neoverse系列的CPU设计。Arm决定加大力度进军服务器和边缘计算市场&#xff0c;专门为这些市场设计Arm CPU内核&#xff0c;而…

CocosCreator3.8研究笔记(十八)CocosCreator UI组件(二)

前面的文章已经介绍了Canvas 组件、UITransform 组件、Widget 组件 。 想了解的朋友&#xff0c;请查看 CocosCreator3.8研究笔记&#xff08;十七&#xff09;CocosCreator UI组件&#xff08;一&#xff09;。 今天我们主要介绍CocosCreator 常用容器组件&#xff1a;Layout …