文章目录
- 一、Java发展史
- 1.1 发展史
- 1.2 OpenJDK和OracleJDK
- 1.3 Open JDK 官网介绍
- 二、Lambda表达式
- 2.1 需求分析
- 2.2 Lamada表达式的体验
- 2.3 Lambda表达式的语法规则
- 2.3.1 Lambda表达式练习
- 2.3.2 Lambda表达式练习
- 2.4 Lambda表达式的使用前提
- 2.5 @FunctionalInterface注解
- 2.6 Lambda表达式的原理
- 2.7 Lambda表达式的省略写法
- 2.8 Lambda表达式和内部类的区别
- 三、接口的增强
- 3.1 JDK8接口新增
- 3.2 默认方法
- 3.3 静态方法
- 3.4 两个方法之间的区别
- 四、函数式接口
- 4.1 函数式接口的由来
- 4.2 函数式接口的介绍
- 4.2.1 Supplier
- 4.2.2 Consumer
- 4.2.3 Function
- 4.2.4 Predicate
- 结语
🌕博客x主页:己不由心王道长🌕!
🌎文章说明:JDK8新特性🌎
✅系列专栏:Java基础
🌴本篇内容:对JDK8的新特性进行学习和讲解🌴
☕️每日一语:这个世界本来就不完美,如果我们再不接受不完美的自己,那我们要怎么活。☕️
🚩 交流社区:己不由心王道长(优质编程社区)
一、Java发展史
1.1 发展史
Sun公司在1991年成立了一个称为绿色计划( Green Project )的项目,由James Gosling(高斯林)博土领导,绿色计划的目的是开发一种能够在各种消费性电子产品(机顶盒、冰箱、收音机等)上运行的程序架构。这个项目的产品就是Java语言的前身: Oak(橡树)。Oak当时在消费品市场上并不算成功,但随着1995年互联网潮流的兴起,Oak迅速找到了最适合自己发展的市场定位。
- JDK Beta - 1995v
- JDK 1.0 - 1996年1月 (真正第一个稳定的版本JDK 1.0.2,被称作 Java 1 )
- JDK 1.1 - 1997年2月
- J2SE 1.2 - 1998年12月
- J2ME(Java 2 Micro Edition,Java 2平台的微型版),应用于移动、无线及有限- 资源的环境。
- J2SE(Java 2 Standard Edition,Java 2平台的标准版),应用于桌面环境。
- J2EE(Java 2 Enterprise Edition,Java 2平台的企业版),应用于基于Java的应用服务器。
- J2SE 1.3 - 2000年5月
- J2SE 1.4 - 2002年2月
- J2SE 5.0 - 2004年9月
- Java SE 6 - 2006年12月
- Java SE 7 - 2011年7月
- Java SE 8(LTS) - 2014年3月
- Java SE 9 - 2017年9月
- Java SE 10(18.3) - 2018年3月
- Java SE 11(18.9 LTS) - 2018年9月
- Java SE 12(19.3) - 2019年3月
- Java SE 13(19.9) - 2019年9月
- Java SE 14(20.3) - 2020年3月
- Java SE 15(20.9) - 2020年9月
我们可以看到Java SE的主要版本大约每两年发布一次,直到Java SE 6到Java SE 7开始花了五年时间,之后又花了三年时间到达Java SE 8。
1.2 OpenJDK和OracleJDK
1.2.1 Open JDK来源:
Java 由 Sun 公司发明,Open JDK是Sun在2006年末把Java开源而形成的项目。也就是说Open JDK是Java SE平台版的开源和免费实现,它由 SUN 和 Java 社区提供支持,2009年 Oracle 收购了 Sun 公司,自此 Java 的维护方之一的SUN 也变成了 Oracle
1.2.2 Open JDK 和 Oracle JDK的关系:
大多数 JDK 都是在 Open JDK 的基础上进一步编写实现的,比如 IBM J9, Oracle JDK 和 Azul Zulu,Azul Zing。
Oracle JDK完全由 Oracle 公司开发,Oracle JDK是基于Open JDK源代码的商业版本。此外,它包含闭源组件。Oracle JDK根据二进制代码许可协议获得许可,在没有商业许可的情况下,在2019年1月之后发布的Oracle Java SE 8的公开更新将无法用于商业或生产用途。但是 Open JDK是完全开源的,可以自由使用。
1.3 Open JDK 官网介绍
Open JDK 官网: http://openjdk.java.net/ 。
JDK Enhancement Proposals(JDK增强建议)。通俗的讲JEP就是JDK的新特性.
小结:
Oracle JDK是基于Open JDK源代码的商业版本。我们要学习Java新技术可以去Open JDK 官网学习。
二、Lambda表达式
在最开始我们先创建一个maven工程,很多东西需要添加依赖
2.1 需求分析
创建一个新的线程,指定线程要执行的任务:
package com.daozhang;
/**
* @Author Administrator
* @Date 2023/6/24 20:19
* @description
* @Version 1.0
*/
public class application {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("测试线程执行代码:"+Thread.currentThread().getName());
}
}).start();
System.out.println("主线程执行的代码:"+Thread.currentThread().getName());
}
}
代码分析:
- Thread类需要一个Runnable接口作为参数(线程的实现有三种实现方式),其中的抽象方法run方法是用来指定线程任务内容的核心。
- 为了指定run方法体,不得不需要Runnable的实现类。
- 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类。
- 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错。
- 而实际上,我们只在乎方法体中的代码。
2.2 Lamada表达式的体验
Lambda表达式是一个匿名函数,可以理解为一段可以传递的代码。
new Thread(() -> { System.out.println("新线程Lambda表达式..."
+Thread.currentThread().getName()); })
.start();
Lambda表达式的优点:简化了匿名内部类的使用,语法更加简单。匿名内部类语法冗余,体验了Lambda表达式后,发现Lambda表达式是简化匿名内部类的一种方式。
2.3 Lambda表达式的语法规则
实际上Lambda省去了面向对象的条条框框,Lambda的标准格式由3个部分组成:
(参数类型 参数名称) -> {
代码体;
}
格式说明:
- (参数类型 参数名称):参数列表
- {代码体;} :方法体
- -> : 箭头,分割参数列表和方法体
2.3.1 Lambda表达式练习
一、练习无参无返回值的Lambda:
1.1 首先定义一个接口:
package com.daozhang.student;
/**
* @Author Administrator
* @Date 2023/6/24 20:31
* @description 定义一个Lambda表达式学生接口
* @Version 1.0
*/
public interface Student {
void gotoSchool();//大学生,主打一个喜欢上学
}
1.2 然后我们在主方法创建使用:
//在这里我们调用loveComeToSchool,首先接口肯定是不能new的,这时候肯定是匿名方式
//1、传统方式(匿名内部类方式)
loveComeToSchool(new Student() {
@Override
public void gotoSchool() {
System.out.println("匿名内部类方式上学:"+Thread.currentThread().getName());
}
});
//2、Lambda表达式方式
loveComeToSchool(()->{
System.out.println("Lambda表达式方式,同样上学");
});
1.3 结果展示:
匿名内部类方式上学:main
Lambda表达式方式,同样上学
1.4小结:可以看到,Lambda表达式十分简洁
2.3.2 Lambda表达式练习
二、练习有参无返回值的Lambda:
Java有两种方法,有参和无参,无参说了,聊聊有参!
2.1、创建对象
2.1.1这里使用到了Lombok,先导入依赖:
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
2.1.2、person类:
package com.daozhang.pojos;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author Administrator
* @Date 2023/6/24 20:47
* @description
* @Version 1.0
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
private String name;
private Integer age;
private Integer high;
}
2.1.3、然后我们在List集合中保存多个Person对象,然后对这些对象做根据age排序操作
package com.daozhang;
import com.daozhang.pojos.Person;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* @Author Administrator
* @Date 2023/6/24 20:47
* @description
* @Version 1.0
*/
public class test02 {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("王道长",18,185));//男神!!!
list.add(new Person("诸葛青",20,183));//不听八卦
list.add(new Person("不要碧莲",19,182));//月下光观鸟
list.add(new Person("宝儿姐",18,168)); //阿威十八式
//使用Collections对list进行排序
//1、传统方式
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
//按照年龄排序
return o1.getAge()-o2.getAge();
}
});
System.out.println("传统:");
for(Person person:list){
System.out.println(person);
}
//2、Lambda方式
Collections.sort(list,(Person o1,Person o2)->{
return o1.getAge()-o2.getAge();
});
System.out.println("Lambda:");
for(Person person:list){
System.out.println(person);
}
}
}
结果:
"C:\Program Files\Java\jdk1.8.0_102\bin\java.exe" "-javaagent:D:\idea\IntelliJ IDEA 2021.3.2\lib\idea_rt.jar=7588:D:\idea\IntelliJ IDEA 2021.3.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_102\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_102\jre\lib\rt.jar;D:\JDK8\JDK8\target\classes" com.daozhang.test02
传统:
Person(name=王道长, age=18, high=185)
Person(name=宝儿姐, age=18, high=168)
Person(name=不要碧莲, age=19, high=182)
Person(name=诸葛青, age=20, high=183)
Lambda:
Person(name=王道长, age=18, high=185)
Person(name=宝儿姐, age=18, high=168)
Person(name=不要碧莲, age=19, high=182)
Person(name=诸葛青, age=20, high=183)
Process finished with exit code 0
2.4 Lambda表达式的使用前提
可以看出,Lambda表达式的语法十分简洁,但是Lambda表达式不是随便使用的,使用的使用有几个条件我们需要特别注意:
一、方法的参数或局部变量类型必须为接口才能使用Lambda。
二、接口中有且只有一个抽象方法(在第五小段会提及一个注解@FunctionalInterfa)
2.5 @FunctionalInterface注解
我们在上面提及,Lambda表达式接口中有且只有一个抽象方法,那么我们怎么做到这一点呢?就是使用FunctionalInterface注解修饰
package com.daozhang.student;
/**
* @Author Administrator
* @Date 2023/6/24 20:31
* @description 定义一个Lambda表达式学生接口
* @Version 1.0
*/
@FunctionalInterface
public interface Student {
/**
* @FunctionalInterface:一个标准注解,被该注解修饰的接口只能声明一个抽象方法
*/
void gotoSchool();
}
2.6 Lambda表达式的原理
匿名内部类的原理:匿名内部类的本质是在编译时生成一个Class 文件。XXXXX$1.class
package com.daozhang;
import com.daozhang.student.Student;
/**
* @Author Administrator
* @Date 2023/6/24 20:19
* @description
* @Version 1.0
*/
public class test01 {
public static void main(String[] args) {
// new Thread(new Runnable() {
// @Override
// public void run() {
// System.out.println("测试线程执行代码:"+Thread.currentThread().getName());
// }
// }).start();
// System.out.println("主线程执行的代码:"+Thread.currentThread().getName());
//在这里我们调用loveComeToSchool,首先接口肯定是不能new的,这时候肯定是匿名方式
//1、传统方式(匿名内部类方式)
loveComeToSchool(new Student() {
@Override
public void gotoSchool() {
System.out.println("匿名内部类方式上学:"+Thread.currentThread().getName());
}
});
//2、Lambda表达式方式
loveComeToSchool(()->{
System.out.println("Lambda表达式方式,同样上学");
});
}
public static void loveComeToSchool(Student student){
student.gotoSchool();
}
}
还可以通过反编译工具来查看生成的代码 XJad 工具来查看:
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space
// Source File Name: test01.java
package com.daozhang;
import com.daozhang.student.Student;
import java.io.PrintStream;
// Referenced classes of package com.daozhang:
// test01
static class test01$1
implements Student
{
public void gotoSchool()
{
System.out.println((new StringBuilder()).append("匿名内部类方式上学:").append(Thread.currentThread().getName()).toString());
}
test01$1()
{
}
}
那么Lambda表达式的原理是什么呢?我们也通过反编译工具来查看:不仅看不了,还关不了!!!
解决办法:ctrl+alt+'.'打开任务管理器,找到对应位置选择结束任务
那我们怎么反编译Lambda表达式的字节码文件呢?,使用JDK自带的javap命令
- javap -c -p 文件名.class
-c:表示对代码进行反汇编
-p:显示所有的类和成员
反汇编结果:
final class com.daozhang.test02$1 implements java.util.Comparator<com.daozhang.pojos.Person> {
com.daozhang.test02$1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int compare(com.daozhang.pojos.Person, com.daozhang.pojos.Person);
Code:
0: aload_1
1: invokevirtual #2 // Method com/daozhang/pojos/Person.getAge:()Ljava/lang/Integer;
4: invokevirtual #3 // Method java/lang/Integer.intValue:()I
7: aload_2
8: invokevirtual #2 // Method com/daozhang/pojos/Person.getAge:()Ljava/lang/Integer;
11: invokevirtual #3 // Method java/lang/Integer.intValue:()I
14: isub
15: ireturn
public int compare(java.lang.Object, java.lang.Object);
Code:
0: aload_0
1: aload_1
2: checkcast #4 // class com/daozhang/pojos/Person
5: aload_2
6: checkcast #4 // class com/daozhang/pojos/Person
9: invokevirtual #5 // Method compare:(Lcom/daozhang/pojos/Person;Lcom/daozhang/pojos/Person;)I
12: ireturn
}
小结:
匿名内部类在编译的时候会产生一个class文件。
Lambda表达式在程序运行的时候会形成一个类。
- 在类中新增了一个方法,这个方法的方法体就是Lambda表达式中的代码。
- 还会形成一个匿名内部类,实现接口,重写抽象方法。
- 在接口中重写方法会调用新生成的方法。
2.7 Lambda表达式的省略写法
在lambda表达式的标准写法基础上,可以使用省略写法的规则为:
- 小括号内的参数类型可以省略
- 如果小括号内有且仅有一个参数,则小括号可以省略
- 如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号。
package com.daozhang;
import com.daozhang.student.Student;
/**
* @Author Administrator
* @Date 2023/6/25 18:52
* @description
* @Version 1.0
*/
public class test03 {
public static void main(String[] args) {
//省略写法一:省略参数、大括号
gotos(()->System.out.println("上学去了"));
}
public static void gotos(Student student){
student.gotoSchool();
}
}
结果:
上学去了
2.8 Lambda表达式和内部类的区别
Lambda和匿名内部类的对比
- 所需类型不一样
匿名内部类的类型可以是 类,抽象类,接口
Lambda表达式需要的类型必须是接口 - 抽象方法的数量不一样
匿名内部类所需的接口中的抽象方法的数量是随意的
Lambda表达式所需的接口中只能有一个抽象方法 - 实现原理不一样
匿名内部类是在编译后形成一个class
Lambda表达式是在程序运行的时候动态生成class
三、接口的增强
3.1 JDK8接口新增
一、JDK8之前:
interface 接口名{
1、静态常量
2、抽象方法
};
二、JDK8之后: 可以有默认方法和静态方法
interface 接口名{
1、静态常量;
2、抽象方法;
3、默认方法;
4、静态方法;
}
3.2 默认方法
一、为什么要有默认方法?
在JDK8以前接口中只能有抽象方法和静态常量,会存在以下的问题:
如果接口中新增抽象方法,那么实现类都必须要抽象这个抽象方法,非常不利于接口的扩展的:
当接口只有一个方法时,实现类去重写:
package com.daozhang;
/**
* @Author Administrator
* @Date 2023/6/25 19:33
* @description
* @Version 1.0
*/
public class test04 {
public static void main(String[] args) {
}
}
interface animal{
void eat();
}
class cat implements animal{
@Override
public void eat() {
}
}
class dog implements animal{
@Override
public void eat() {
}
}
当接口有很多方法时,实现类也可以重写,但是每一个实现类都去重写它不需要的方法,这合理吗?:
package com.daozhang;
/**
* @Author Administrator
* @Date 2023/6/25 19:33
* @description
* @Version 1.0
*/
public class test04 {
public static void main(String[] args) {
}
}
interface animal{
void run();
void sing();
void eat();
}
class cat implements animal{
//必须重写,不然报错
@Override
public void eat() {
}
}
class dog implements animal{
//必须重写,不然报错
@Override
public void eat() {
}
}
可以看出,上面接口显然不符合我们的要求,所以在JDK8之后,就引入(增加)了默认方法。
二、默认方法的格式
以下是默认方法的格式:
interface 接口名{
修饰符 default 返回值类型 方法名{
方法体;
}
}
默认方法就是你想重写就重写,不重写也不会强制你实现
package com.daozhang;
/**
* @Author Administrator
* @Date 2023/6/25 19:33
* @description
* @Version 1.0
*/
public class test04 {
public static void main(String[] args) {
animal cat = new cat();
cat.gotoSchool();
}
}
interface animal{
void eat();
public default String gotoSchool(){
System.out.println("谁说动物不用上学的?");
return null;
}
}
class cat implements animal{
@Override
public void eat() {
}
}
class dog implements animal{
@Override
public void eat() {
}
}
二、接口中默认方法的使用:
- 接口中的默认方法有两种使用方式
-
- 实现类直接调用接口的默认方法
-
- 实现类重写接口的默认方法
3.3 静态方法
一、在JDK8中在接口中增加了静态方法,作用同样是为了增加接口的扩展性
二、语法规则:
interface 接口名{
修饰符 static 返回值类型 方法名{
方法体;
}
}
package com.daozhang;
/**
* @Author Administrator
* @Date 2023/6/25 19:33
* @description
* @Version 1.0
*/
public class test04 {
public static void main(String[] args) {
animal cat = new cat();
cat.gotoSchool();
}
}
interface animal{
void eat();
public default String gotoSchool(){
System.out.println("谁说动物不用上学的?");
return null;
}
public static void makeFriend(){
System.out.println("想找个女朋友");
}
}
class cat implements animal{
@Override
public void eat() {
}
}
class dog implements animal{
@Override
public void eat() {
}
}
静态方法的使用:
接口中的静态方法在实现类中是不能被重写的,调用的话只能通过接口类型来实现: 接口名.静态方法名();
注意:这里是不能重写,而不是不需要
3.4 两个方法之间的区别
- 默认方法通过实例调用,静态方法通过接口名调用
- 默认方法可以被继承,实现类可以直接调用接口默认方法,也可以重写接口默认方法
- 静态方法不能被继承,实现类不能重写接口的静态方法,只能使用接口名调用
四、函数式接口
4.1 函数式接口的由来
我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方法,在JDK中提供了大量常用的函数式接口。
/**
* @Author Administrator
* @Date 2023/6/25 21:01
* @description
* @Version 1.0
*/
public class functionTest01 {
public static void main(String[] args) {
funSum(arr->{
int sum = 0;
for(int i:arr){
sum+=i;
}
return sum;
});
}
public static void funSum(Operator operator){
int[] arr ={5,6,7,8,9};
int sum = operator.getArr(arr);
System.out.println(sum);
}
}
/*
* 函数式接口
*/
@FunctionalInterface
interface Operator{
int getArr(int[] arr);
}
4.2 函数式接口的介绍
在JDK中帮我们提供的有函数式接口,主要是在 java.util.function 包中。
4.2.1 Supplier
一、无参有返回值的接口,对于的Lambda表达式需要提供一个返回数据的类型。
/**
* Represents a supplier of results.
*
* <p>There is no requirement that a new or distinct result be returned each
* time the supplier is invoked.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #get()}.
*
* @param <T> the type of results supplied by this supplier
*
* @since 1.8
*/
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
二、简单使用
/**
* @Author Administrator
* @Date 2023/6/25 21:17
* @description
* @Version 1.0
*/
public class Supplier {
public static void main(String[] args) {
fun(()->{
int[] arr = {250,361,520};
Arrays.sort(arr);
return arr[arr.length-1];
});
}
public static void fun(java.util.function.Supplier<Integer> supplier){
Integer number = supplier.get();
System.out.println(number);
}
}
4.2.2 Consumer
一、有参无返回值得接口,前面介绍的Supplier接口是用来生产数据的,而Consumer接口是用来消费数据的,使用的时候需要指定一个泛型来定义参数类型。
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
二、简单使用
/**
* @Author Administrator
* @Date 2023/6/25 21:49
* @description
* @Version 1.0
*/
public class ConsumerTest {
public static void main(String[] args) {
fun2((t)->{
//在方法体有动作才会执行方法,否则不会执行方法
System.out.println(t);
});
}
public static void fun2(Consumer<String> consumer){
consumer.accept("唱跳rap");
}
}
默认方法:andThen
如果一个方法的参数和返回值全部是Consumer类型,那么就可以实现效果,消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合,而这个方法就是Consumer接口中的default方法
andThen方法
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
测试:
/**
* @Author Administrator
* @Date 2023/6/25 22:08
* @description
* @Version 1.0
*/
public class ConsumerAndThenTest {
public static void main(String[] args) {
Test2(msg1->{
//转大写
System.out.println(msg1.toUpperCase());
},msg2->{
//转小写
System.out.println(msg2.toLowerCase());
});
}
public static void Test2(Consumer<String> t1,Consumer<String> t2){
String str = "Out of control";
t2.andThen(t1).accept(str);
}
}
结果:
out of control
OUT OF CONTROL
分析,观察到与我们在调用的时候写的顺序不一样,为什么呢?
原理:
我们在Test2方法里使用的是t2.andThen(t1),也就是在t2作为参数传递执行对应的方法之后执行t1,andThen的意思就是什么之后做这件事情。
4.2.3 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);
使用:输入一个字符串,返回一个整数。
/**
* @Author Administrator
* @Date 2023/6/25 22:35
* @description
* @Version 1.0
*/
public class FunctionTest {
public static void main(String[] args) {
Test04((arg)->{
Integer res = Integer.valueOf(arg);
return res;
});
}
public static void Test04(Function<String,Integer> function){
int gif = function.apply("18");
System.out.println(gif);
}
}
结果:
18
默认方法:andThen,也是用来进行组合操作。
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
使用:
/**
* @Author Administrator
* @Date 2023/6/25 22:49
* @description
* @Version 1.0
*/
public class FunctionAndThenTest {
public static void main(String[] args) {
fun(arg1->{
return Integer.valueOf(arg1);
},arg2->{
return arg2*10;
});
}
public static void fun(Function<String,Integer> t1,Function<Integer,Integer> t2){
int res = t1.andThen(t2).apply("18");
System.out.println(res);
}
}
注意,由于t1——》t2具有传递性,所有t2的 泛型要跟t1转换后的泛型对应
默认的compose方法的作用顺序和andThen方法刚好相反
而静态方法identity则是,输入什么参数就返回什么参数
4.2.4 Predicate
有参且返回值为Boolean的接口
/**
* 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);
使用:
/**
* @Author Administrator
* @Date 2023/6/25 23:00
* @description
* @Version 1.0
*/
public class PredicateTest {
public static void main(String[] args) {
fun((arg)->{
boolean res = arg.equals("不需要吗?");
return res;
});
}
public static void fun(Predicate<String> msg){
boolean res = msg.test("爱一个人需要理由吗?");
System.out.println(res);
}
}
其他扩展方法:
/**
* Returns a predicate that represents the logical negation of this
* predicate.
*
* @return a predicate that represents the logical negation of this
* predicate
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* Returns a composed predicate that represents a short-circuiting logical
* OR of this predicate and another. When evaluating the composed
* predicate, if this predicate is {@code true}, then the {@code other}
* predicate is not evaluated.
*
* <p>Any exceptions thrown during evaluation of either predicate are relayed
* to the caller; if evaluation of this predicate throws an exception, the
* {@code other} predicate will not be evaluated.
*
* @param other a predicate that will be logically-ORed with this
* predicate
* @return a composed predicate that represents the short-circuiting logical
* OR of this predicate and the {@code other} predicate
* @throws NullPointerException if other is null
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* Returns a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}.
*
* @param <T> the type of arguments to the predicate
* @param targetRef the object reference with which to compare for equality,
* which may be {@code null}
* @return a predicate that tests if two arguments are equal according
* to {@link Objects#equals(Object, Object)}
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
在Predicate中的默认方法提供了逻辑关系操作 and or negate isEquals方法:
使用:
/**
* @Author Administrator
* @Date 2023/6/25 23:09
* @description
* @Version 1.0
*/
public class PredicateDemoTest {
public static void main(String[] args) {
fun(arg1->{
return arg1.contains("爱");
},arg2->{
return arg2.contains("pf");
});
}
public static void fun(Predicate<String> arg1,Predicate<String> arg2){
boolean And = arg1.and(arg2).test("不说一句的爱有多好");
boolean Or = arg1.or(arg2).test("不说一句的爱有多好");
boolean Negate = arg1.negate().test("不说一句的爱有多好");
System.out.println(And);
System.out.println(Or);
System.out.println(Negate);
}
}
结果:
false
true
false
结语
写到这里总字数也有两万+了,然后觉得文章太长,看的人恐怕不多,一般就看看就划走了,故而分为上下两部,上部就先到函数式接口。归根结底,只有练习才能真正理解用法和体会这种思维,加油。