JDK8新特性-上部

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

代码分析:

  1. Thread类需要一个Runnable接口作为参数(线程的实现有三种实现方式),其中的抽象方法run方法是用来指定线程任务内容的核心。
  2. 为了指定run方法体,不得不需要Runnable的实现类。
  3. 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类。
  4. 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错。
  5. 而实际上,我们只在乎方法体中的代码。

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表达式在程序运行的时候会形成一个类。

  1. 在类中新增了一个方法,这个方法的方法体就是Lambda表达式中的代码。
  2. 还会形成一个匿名内部类,实现接口,重写抽象方法。
  3. 在接口中重写方法会调用新生成的方法。

2.7 Lambda表达式的省略写法

在lambda表达式的标准写法基础上,可以使用省略写法的规则为:

  1. 小括号内的参数类型可以省略
  2. 如果小括号内有且仅有一个参数,则小括号可以省略
  3. 如果大括号内有且仅有一个语句,可以同时省略大括号,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和匿名内部类的对比

  1. 所需类型不一样
    匿名内部类的类型可以是 类,抽象类,接口
    Lambda表达式需要的类型必须是接口
  2. 抽象方法的数量不一样
    匿名内部类所需的接口中的抽象方法的数量是随意的
    Lambda表达式所需的接口中只能有一个抽象方法
  3. 实现原理不一样
    匿名内部类是在编译后形成一个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() {
    }
}

二、接口中默认方法的使用:

  • 接口中的默认方法有两种使用方式
    1. 实现类直接调用接口的默认方法
    1. 实现类重写接口的默认方法

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 两个方法之间的区别

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

四、函数式接口

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

结语

写到这里总字数也有两万+了,然后觉得文章太长,看的人恐怕不多,一般就看看就划走了,故而分为上下两部,上部就先到函数式接口。归根结底,只有练习才能真正理解用法和体会这种思维,加油。

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_63992577

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值