Java8新特性 - Lambda表达式

news2024/11/26 15:07:33

目录

一、Lambda表达式

1.1、为什么使用Lambda表达式?

1.2、Lambda的标准格式

Lambda的标准格式

无参无返回值的Lambda

有参有返回值的Lambda

1.3、Lambda的实现原理

1.4、Lambda省略模式

1.5、Lambda表达式的前提条件

1.6、Lambda与匿名内部类对比

1.7、JDK8接口新增的两个方法

JDK8接口增强介绍

接口默认方法的定义格式

接口默认方法的使用

接口静态方法的定义格式

接口静态方法的使用

接口静态方法与默认方法的区别

1.8、常用内置函数式接口

Supplier

Consumer

Function

Predicate

1.8、方法引用

Lambda的冗余场景

常见引用方式

对象名::引用成员方法

类名::引用静态方法

类名::引用实例方法

类名::new引用构造器

数组::new引用数组构造器


一、Lambda表达式

1.1、为什么使用Lambda表达式?

Lambda是一个匿名函数,我们可以把Lambda表达式理解为一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

匿名内部类与Lambda表达式写法对比:显而易见Lambda表达式更加简便。

public class TestLambda {
    public static void main(String[] args) {
        //使用匿名内部类
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("普通匿名内部类执行了");
            }
        }).start();

      
        //使用Lambda
        new Thread(()->{
            System.out.println("Lambda表达式执行了");
        }).start();
    }
}

1.2、Lambda的标准格式

Lambda的标准格式

(参数类型 参数名称) -> {
    代码体;
}

无参无返回值的Lambda

public interface Swimmable {
    public abstract void swimming();
}
public class TestLambda2 {
    public static void main(String[] args) {
        //无参无返回值的Lambda
        goSwimming(()->{
            System.out.println("无参无返回值的Lambda");
        });
    }

    public static void goSwimming(Swimmable s){
        s.swimming();
    }
}

有参有返回值的Lambda

public interface Swimmable {
    public abstract int swimming(String name);
}
public class TestLambda2 {
    public static void main(String[] args) {
        //有参有返回值的Lambda
        goSwimming((String name)->{
            System.out.println(name + "正在游泳");
            return 10;
        });
    }

    public static void goSwimming(Swimmable s){
        s.swimming("尼采");
    }
}

1.3、Lambda的实现原理

普通的匿名内部类编译后是会生成一个名字带$的类,但Lambda不会生成其它类文件,但是Lambda表达式会在这个类中新生成一个私有的静态方法,并且Lambda表达式的代码会放到这个新增的方法中。同时Lambda底层还是会以普通匿名内部类的形式去展现,只不过在内部类中调用了之前生成的私有的静态方法。

1.4、Lambda省略模式

在Lambda标准格式的基础上,使用省略写法的规则为:

1、小括号内参数的类型可以省略。

2、如果小括号内有且仅有一个参数,则小括号可以省略。

3、如果大括号内有且仅有一个语句,可以同时省略大括号、return关键字及语句分号。

public static void main(String[] args) {
    List<String> books = new ArrayList<>();
    books.add("百年孤独");
    books.add("霍乱时期的爱情");
    books.add("一桩事先张扬的凶杀案");
    books.add("没有人给他写信的上校");

    //省略前
    books.forEach((t) -> {
        System.out.println(t);
    });

    //省略后
    books.forEach(t -> System.out.println(t));
}

        

1.5、Lambda表达式的前提条件

1、方法的参数或局部变量类型必须为接口才能使用Lambda。

2、接口中有且仅有一个抽象方法(函数式接口)。

1.6、Lambda与匿名内部类对比

1、所需的类型不一样:

匿名内部类:需要的类型可以是类、抽象类、接口。

Lambda:需要的类型必须是接口。

2、抽象方法的数量不一样:

匿名内部类:所需的接口中抽象方法的数量随意。

Lambda:表达式所需的接口只能有一个抽象方法。

3、实现原理不同:

匿名内部类:在编译后会形成class。

Lambda:在程序运行的时候动态生成class。

1.7、JDK8接口新增的两个方法

JDK8接口增强介绍

JDK8以前的接口:

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

JDK8的接口:

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

接口中的默认方法实现类不需要重写,可以直接使用,实现类也可以根据需要重写。这样就方便接口的扩展。

接口默认方法的定义格式

interface 接口名 {
    public default void method(){
        //代码
    }
}

接口默认方法的使用

public interface Swimmable {
    public default void eat(String name){
        System.out.println(name + "在吃饭");
    }
}

class SwimmableImpl implements Swimmable{

}
public class TestLambda2 {
    public static void main(String[] args) {
        Swimmable sw = new SwimmableImpl();
        sw.eat("马尔克斯");
    }
}

接口静态方法的定义格式

interface 接口名 {
    修饰符 static 返回值类型 方法名(){
         //代码   
    }
}

接口静态方法的使用

public interface Swimmable {
    public static void eat(String name){
        System.out.println(name + "在吃饭");
    }
}

class SwimmableImpl implements Swimmable{

}
public class TestLambda2 {
    public static void main(String[] args) {
        Swimmable.eat("马尔克斯"); //接口的静态方法只能用接口名调用
    }
}

接口静态方法与默认方法的区别

1、默认方法通过实例调用,静态方法通过接口名调用。

2、默认方法可以被继承,实现类可以直接使用接口默认方法,也可以重写接口默认方法。

3、静态方法不能被继承,实现类不能重写接口的静态方法,只能使用接口名调用。

1.8、常用内置函数式接口

他们主要在java.util.function包中。下面是最常用的几个接口:

Supplier

供给型接口,对应的Lambda表达式需要“对外提供”一个符合泛型类型的数据对象。

@FuncationalInterface
public interface Supplier<T> {
  public abstract T get();  
}

实例:返回数组元素最大值

public class TestLambda2 {
    public static void main(String[] args) {
        int[] arr = {5,8,1,20,2,17};
        printMax(() -> {
            Arrays.sort(arr);
            return arr[arr.length - 1];
        });
    }

    public static void printMax(Supplier<Integer> supplier){
        int max = supplier.get();
        System.out.println(max);
    }
}

Consumer

与Supplier接口正好相反,它不是生产一个数据,而是消费一个数据。

@FuncationalInterface
public interface Consumer<T> {
  public abstract void accept(T t);  
}

实例:将一个字符串转成大写和小写的字符串

public class TestLambda2 {
    public static void main(String[] args) {
        printHello((String str) -> {
            System.out.println(str.toUpperCase());
        });
    }

    public static void printHello(Consumer<String> consumer){
        consumer.accept("hello");
    }
}

Function

java.util.function.Function<T,R>接口用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。有参数有返回值。

@FuncationalInterface
public interface Function<T,R> {
  public abstract R apply(T t);  
}

实例:将字符串转成数字

public class TestLambda2 {
    public static void main(String[] args) {
        getNumber((String str) -> {
            Integer i = Integer.parseInt(str);
            return i;
        });
    }

    public static void getNumber(Function<String,Integer> function){
        Integer num = function.apply("10");
        System.out.println(num);
    }
}

Predicate

有时候我们需要对某种类型进行判断,从而得到一个boolean值结果。

@FuncationalInterface
public interface Predicate<T> {
  public abstract boolean test(T t);  
}

实例1:判断一个人名如果超过3个字就认为是很长的名字

public class TestLambda2 {
    public static void main(String[] args) {
        isLongName((String name) -> {
            return name.length() > 3;
        });
    }

    public static void isLongName(Predicate<String> predicate){
        boolean b = predicate.test("马尔克斯");
        System.out.println(b);
    }
}

实例2:判断一个字符串中包含W,也包含H

public class TestLambda2 {
    public static void main(String[] args) {
        exercise((String str) -> {
            return str.contains("H");
        },(String str) -> {
            return str.contains("W");
        });
    }

    public static void exercise(Predicate<String> p1,Predicate<String> p2){
        boolean b1 = p1.test("Hello");
        boolean b2 = p2.test("World");
        if(b1 && b2){
            System.out.println("既包含W,也包含H");
        }
    }
}

使用and可以将代码改写为:

public class TestLambda2 {
    public static void main(String[] args) {
        exercise((String str) -> {
            return str.contains("H");
        },(String str) -> {
            return str.contains("W");
        });
    }

    public static void exercise(Predicate<String> p1,Predicate<String> p2){
        String str = "HelloWorld";
        boolean b = p1.and(p2).test(str);
        if(b)
            System.out.println("既包含W,也包含H");
    }
}

1.8、方法引用

Lambda的冗余场景

public class Y {
    public static void main(String[] args) {
        //使用方法引用
        //这个指定的方法getMax去重写接口的抽象方法accept,到时候调用接口的抽象方法就是调用传递过去的方法
        printMax(Y::getMax);
    }

    public static void getMax(int[] arr){
        int sum = 0;
        for (int n : arr){
            sum += n;
        }
        System.out.println(sum);
    }

    public static void printMax(Consumer<int[]> consumer){
        int[] arr = {11,22,33,44,55};
        consumer.accept(arr);
    }
}

常见引用方式

1、对象::方法名

2、类名::静态方法

3、类名::普通方法

4、类名::构造器

5、String[]::数组构造器

对象名::引用成员方法

//正常使用Lambda
@Test
public void test01(){
    Date now = new Date();
    Supplier<Long> su1 = () -> {
        return now.getTime();
    };
    Long aLong = su1.get();
    System.out.println(aLong);
}
//对象引用Lambda
@Test
public void test02(){
    Date now = new Date();
    Supplier<Long> su2 = now::getTime;
    Long aLong = su2.get();
    System.out.println(aLong);
}

注意:你调用方法的参数和返回值要和接口中抽象方法的返回值和参数要一致比如上述代码,getTime()方法参数是无参,返回值是Long类型,那我们接口中的get()方法也是一样的。


类名::引用静态方法

@Test
public void test01(){
    Supplier<Long> sup = System::currentTimeMillis;
    Long aLong = sup.get();
    System.out.println(aLong);
}

类名::引用实例方法

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

当调用的方法无参时:

@Test
public void test01(){
    Function<String,Integer> fun = String::length;
    Integer length = fun.apply("Hello");
    System.out.println(length);
}

当方法有一个参数时:

@Test
public void test02(){
    BiFunction<String,Integer,String> fun = String::substring;
    String result = fun.apply("HelloWorld", 3);
    System.out.println(result);
}

类名::new引用构造器

由于构造器的名称与类名完全一样,所以构造器引用使用类名称::new的格式表示。

@Test
public void test01(){
    Supplier<Person> sup = Person::new;
    Person person = sup.get();
    System.out.println(person);
}

数组::new引用数组构造器

数组也是Object的子类对象,所以同样具有构造器,只是语法稍有不同。

@Test
public void test01(){
    Function<Integer,int[]> fun = int[]::new;
    int[] apply = fun.apply(5);
    System.out.println(apply);
}

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

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

相关文章

AI智剪,批量剪辑视频的神器

在数字时代&#xff0c;视频剪辑已经成为各种行业中的重要工作。然而&#xff0c;传统的视频剪辑方式既耗时又费力&#xff0c;常常需要大量的时间和人力。幸运的是&#xff0c;随着人工智能技术的发展&#xff0c;我们有了新的解决方案——AI智剪软件。 AI智剪软件&#xff0c…

【gtpJavaScript】使用JavaScript实现套壳gtp与gtp打字输出效果

postman测试gtp接口 https://platform.openai.com/docs/api-reference/chat/create?langcurl 导入到postman中 记得弄一个gtp的key 然后请求测试gtp接口&#xff1a; 纯前端实现gtp请求页面 目录结构&#xff1a; 部分参考&#xff1a;GitHub - xxxjkk/chat-website: 简易版c…

德庄借助纷享销客CRM系统实现高效管理

德庄集团创于1999年&#xff0c;是一家集餐饮产业、食品产业、科技研发及文化研究为一体的现代化民营企业&#xff0c;下属9家子公司、2大现代化食品加工基地、1所研究所、1所培训学校、1个技术中心。拥有德庄、青一色、滟设、香漫谷、饭空等8大子品牌&#xff0c;呈现出良好的…

基于Python的大区域SPI标准降水指数自动批量化处理

1.引言 标准化降水指数&#xff08;SPI&#xff09;是一个广泛使用的指数&#xff0c;用于描述一系列时间尺度上的气象干旱的特征。但是经过研究发现&#xff0c;目前的处理方法基本都是单点进行计算&#xff0c;缺少多点&#xff08;大区域&#xff09;的批量计算过程。因此本…

嵌入式Linux开发实操(十六):Linux驱动模型driver model

嵌入式linux下驱动模型: 1、驱动的绑定 驱动程序绑定driver binding 驱动程序绑定是将设备device与可以控制它的设备驱动程序driver相关联的过程。总线驱动程序bus driver通常会处理,因为有特定于总线bus的结构来表示设备device和驱动程序driver。使用通用的设备device和设…

es倒排索引深入解读

文章目录 一. Lucene二.倒排索引算法2.1 Posting List压缩算法2.1.1 FOR2.1.2 RoaringBitmap压缩 2.3 FST压缩算法2.3.1 trie前缀树原理2.3.2 FST构建过程NFADFAFSMFSAFST:有限状态转换机构建原理FST在lucene中实现原理 1.什么是搜索引擎? 全文搜索引擎: 自然语言处理(NLP)、爬…

Full authentication is required to access this resource解决办法

我们在使用postman调接口时候&#xff0c;有的时候需要权限才可以访问&#xff0c;否则可能会报下面这个错误 {"timestamp": xxxxxx,"status": 401,"error": "Unauthorized","message": "Full authentication is requ…

Matlab信号处理1:模拟去除信号噪声

由于工作内容涉及信号系统、信号处理相关知识&#xff0c;本人本硕均为计算机相关专业&#xff0c;专业、研究方向均未涉及信号相关知识&#xff0c;因此需进行系统地学习。之前已将《信号与系统》快速过了一遍&#xff0c;但感觉较抽象且理解较浅显。在此系统地学习如何使用Ma…

PTA L1-011 A-B C++解法

我的答案 #include<iostream> #include <string> using namespace std;int main() {//先用数组去存储输入的A和B&#xff0c;然后遍历数组A&#xff0c;B&#xff0c;相同的字母去除&#xff0c;不同的字母留下&#xff0c;最后输出string A, B;getline(cin, A);g…

Nomad 系列-安装

系列文章 Nomad 系列文章 Nomad 简介 开新坑&#xff01;近期算是把自己的家庭实验室环境初步搞好了&#xff0c;终于可以开始进入正题研究了。 首先开始的是 HashiCorp Nomad 系列&#xff0c;欢迎阅读。 关于 Nomad 的简介&#xff0c;之前在 大规模 IoT 边缘容器集群管…

java IO流(二) 字符流 缓冲流 原始流与缓冲流性能分析

字符流 前面学习的字节流虽然可以读取文件中的字节数据&#xff0c;但是如果文件中有中文&#xff0c;使用字节流来读取&#xff0c;就有可能读到半个汉字的情况&#xff0c;这样会导致乱码。虽然使用读取全部字节的方法不会出现乱码&#xff0c;但是如果文件过大又不太合适。…

递归入门,例题详解,汉诺塔问题,全排列问题,整数划分问题,两数相加

问题一&#xff1a;阶乘 对于阶乘n!&#xff0c;也就是从1一直乘到n&#xff0c;我们可以很简单的使用一个for循环来解决这个问题&#xff0c;但是如果使用递归的思路&#xff0c;那么我们需要思考如果将当前的问题分解为规模更小的问题&#xff0c;对于n的阶乘&#xff0c;我…

论文解读 | KPConv——点云上的可形变卷积网络

原创 | 文 BFT机器人 《KPConv: Flexible and Deformable Convolution for Point Clouds》是一篇发表于2019年的研究论文&#xff0c;作者为Hugues Thomas、Charles R. Qi、Jean-Emmanuel Deschaud、Beatriz Marcotegui和Franois Goulette。这篇论文关注于点云数据上的卷积操作…

前置微小信号放大器是什么

前置微小信号放大器是一种专门用于放大微弱输入信号的电子设备。它常用于电子测量、信号传输、音频放大等领域&#xff0c;能够将微小的输入信号放大到足够大的幅度&#xff0c;以便后续处理或传输。下面我们将从工作原理、应用和发展趋势三个方面&#xff0c;详细探讨前置微小…

在国内PMP的含金量高吗?

首先我们需要了解一下PMP证书的用处 PMP含金量是毋庸置疑的&#xff0c;从事项目/产品/运营/管理/IT行业的社会人基本都会将这个证收入囊中。其他有升职涨薪计划的也在悄咪咪报考蓄力中&#xff0c;能在职业生涯锦上添花&#xff0c;精益求精。 一&#xff0c;PMP证书的优势体…

什么是安全运营中心(SOC),应该了解什么

安全运营中心&#xff08;SOC&#xff09; 是一种企业监视和警报设施&#xff0c;可帮助组织检测安全威胁、监视安全事件和分析性能数据以改进公司运营。 什么是安全运营中心&#xff08;SOC&#xff09; 安全运营中心&#xff08;SOC&#xff09;是一个中央监视和监视中心&a…

安装气象站的重要意义是什么?

气象站是一种用于观测、记录和报告天气数据的设备或系统。它通常包括各种传感器、供电系统和环境监控主机&#xff0c;用于测量和记录气温、湿度、风速、风向、气压、降雨量、雪深等气象参数。 气象站有多种类型&#xff0c;包括自动气象站、人工气象站和便携式气象站。自动气…

elementUI可拖拉宽度抽屉

1&#xff0c;需求&#xff1a; 在elementUI的抽屉基础上&#xff0c;添加可拖动侧边栏宽度的功能&#xff0c;实现效果如下&#xff1a; 2&#xff0c;在原组件上添加自定义命令 <el-drawer v-drawerDrag"left" :visible.sync"drawerVisible" direc…

【狂神】SpringMVC 怎样才能直接手动输入.jsp的页面就可以访问了?

看秦老师视频的时候&#xff0c;觉得非常疑惑&#xff0c;为什么可以直接输入form.jsp就能跳转到相应地页面。如果你和我一样眼瞎&#xff0c;那确实是有点崩溃。注意看&#xff1a; 发现了吗&#xff1f;这几个文件并没有放在WEB-INF文件下&#xff0c;所以视图解析器便不生效…

实用技巧:使用Python进行文本处理

引言 作为一个Linux持续学习者&#xff0c;我们经常需要处理文本文件&#xff0c;例如提取特定内容、格式化数据或者进行文本分析等。在这篇文章中&#xff0c;我将介绍使用Python进行文本处理的一些实用技巧&#xff0c;帮助你更有效地处理文本数据。无需担心&#xff0c;你不…