「 Java基础-Lambda 」试试Lambda表达式?通俗易懂得嘞!

简介: 本文从Lambda表达式的基础概念、函数式接口、以及Lambda表达式的常用示例几方面完整的讨论了这一Java8新增的特性,实际开发中确实为我们提供了许多便利,简化了代码。欢迎小伙伴继续提出不同的见解一起讨论!

前言

Lambda表达式是JDK8的一个新特性,可以取代大部分的匿名内部类,写出更优雅的Java代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构

JDK也提供了大量的内置函数式接口供我们使用,使得Lambda表达式的运用更加方便、高效

一、什么是Lambda表达式

Lambda表达式,也称为闭包java8的新特性,lambda运行将函数作为一个方法的参数,也就是将函数作为参数传递到方法中。使用lambda表达式可以让代码更加简洁。

Lambda表达式常用于简化接口实现,关于接口实现,可以有很多种方式。例如:

  1. 创建接口的实现类;
  2. 使用匿名内部类;

但是lambda表达式,比这两种方式都简单。代码示例如下:

interfaceTestInterface{
publicvoidtestFun();
}
publicclassTestClass {
publicstaticvoidmain(String[] args) { 
//使用lambda表达式实现接口TestClasstest= () -> {
System.out.println("test");
        };
test.testFun();
    }
}

二、使用前提

上文中提到,lambda表达式可以在⼀定程度上简化接口的实现。但是,并不是所有的接口都可以使用lambda表达式来简化接口的实现的。

先说结论,lambda表达式,只能实现函数式接口lambda表达式毕竟只是⼀个匿名方法

三、函数式接口

3.1 概念

函数式接口在 Java 中是指: 有且仅有一个抽象方法的接口

函数式接口,即适用于函数式编程场景的接口。而 Java 中的函数式编程体现就是Lambda,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的 Lambda才能顺利地进行推导。

备注:

语法糖 是指使用更加方便,但是原理不变的代码语法。

例如:

在遍历集合时使用的for-each语法,其实底层的实现原理仍然是迭代器,这便是语法糖。从应用层面来讲, Java 中的 Lambda 可以被当做是匿名内部类的语法糖,但是二者在原理上是不同的。

3.2 格式

只要确保接口中有且仅有一个抽象方法即可,伪代码如下:

修饰符interface接口名称 {
publicabstract返回值类型方法名称(可选参数信息);
// 其它非抽象方法内容}

由于接口当中抽象方法的public abstract是可以省略的,所以定义一个函数式接口很简单:

publicinterfaceTestFunctionalInterface {
voidtestMethod();
}

3.3 @FunctionalInterface

@Override 注解的作用类似, Java 8 中专门为函数式接口引入了一个新的注解:@FunctionalInterface。该注解可用于一个接口的定义上:

@FunctionalInterfacepublicinterfaceTestFunctionalInterface {
voidtestMethod();
}

一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。需要注意的是,即使不使用该注解,只要满足函数式接口的定义,这仍然是一个函数式接口,使用起来都一样。

3.4 自定义函数式接口

对于刚刚定义好的 MyFunctionalInterface 函数式接口,典型使用场景就是作为方法的参数:

publicclassTestFunctionalClass {
// 使用自定义的函数式接口作为方法参数privatestaticvoiddoSomeThing(TestFunctionalInterfacetestInterFace){
testInterFace.testMethod();
    }
publicstaticvoidmain(String[] args) {
// 调用函数式接口的方法doSomeThing(() ->System.out.println("Hello world!"));
    }
}

四 语法格式

4.1 基础语法

lambda表达式,其实本质来讲,就是⼀个匿名函数。因此在写lambda表达式的时候,不需要关心方法名是什么。

实际上,我们在写lambda表达式的时候,也不需要关心返回值类型,只需要关注两部分内容即可:参数列表和方法体

(参数1,参数2,…) -> {

方法体  

};

各部分详述

参数部分:方法的参数列表,要求和实现的接口中的方法参数部分⼀致,包括参数的数量和类型。

方法体部分 : 方法的实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回。

->: 分隔参数部分和方法体部分。

总结来说:语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)。

4.2 重要特征

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

代码示例:

publicclassDemo01 {
/**多参数无返回*/@FunctionalInterfacepublicinterfaceNoReturnMultiParam {
voidmethod(inta, intb);
    }
/**无参无返回值*/@FunctionalInterfacepublicinterfaceNoReturnNoParam {
voidmethod();
    }
/**一个参数无返回*/@FunctionalInterfacepublicinterfaceNoReturnOneParam {
voidmethod(inta);
    }
/**多个参数有返回值*/@FunctionalInterfacepublicinterfaceReturnMultiParam {
intmethod(inta, intb);
    }
/*** 无参有返回*/@FunctionalInterfacepublicinterfaceReturnNoParam {
intmethod();
    }
/**一个参数有返回值*/@FunctionalInterfacepublicinterfaceReturnOneParam {
intmethod(inta);
    }
publicstaticvoidmain(String[] args) {
//无参无返回NoReturnNoParamnoReturnNoParam= () -> {
System.out.println("NoReturnNoParam");
        };
noReturnNoParam.method();
//一个参数无返回NoReturnOneParamnoReturnOneParam= (inta) -> {
System.out.println("NoReturnOneParam param:"+a);
        };
noReturnOneParam.method(6);
//多个参数无返回NoReturnMultiParamnoReturnMultiParam= (inta, intb) -> {
System.out.println("NoReturnMultiParam param:"+"{"+a+","++b+"}");
        };
noReturnMultiParam.method(6, 8);
//无参有返回值ReturnNoParamreturnNoParam= () -> {
System.out.print("ReturnNoParam");
return1;
        };
intres=returnNoParam.method();
System.out.println("return:"+res);
//一个参数有返回值ReturnOneParamreturnOneParam= (inta) -> {
System.out.println("ReturnOneParam param:"+a);
return1;
        };
intres2=returnOneParam.method(6);
System.out.println("return:"+res2);
//多个参数有返回值ReturnMultiParamreturnMultiParam= (inta, intb) -> {
System.out.println("ReturnMultiParam param:"+"{"+a+","+b+"}");
return1;
        };
intres3=returnMultiParam.method(6, 8);
System.out.println("return:"+res3);
    }
}

控制台输出:

NoReturnNoParamNoReturnOneParamparam:6NoReturnMultiParamparam:{6,8}
ReturnNoParamreturn:1ReturnOneParamparam:6return:1ReturnMultiParamparam:{6,8}
return:1

五 语法简化

5.1 参数部分的精简

5.1.1 参数的类型

由于在接口的方法中,已经定义了每⼀个参数的类型是什么。而且在使用lambda表达式实现接口的时候,必须要保证参数的数量和类 型需要和接口中的方法保持⼀致。因此,此时lambda表达式中的参数的类型可以省略不写。

注意事项:

如果需要省略参数的类型,要保证:要省略, 每⼀个参数的类型都必须省略不写。绝对不能出现,有的参数类型省略了,有的参数类型没有省略。

// 有参+返回值Testtest= (name,age)  -> {
System.out.println(name+age+"了!");
returnage+1;
};
intage=test.test("小刘学编程",18);
System.out.println(age);

5.1.2 参数的小括号

如果方法的参数列表中的参数数量 有且只有⼀个,此时,参数列表的小括号是可以省略不写的。

注意事项:

  • 只有当参数的数量是⼀个的时候, 多了、少了都不能省略。
  • 省略掉小括号的同时, 必须要省略参数的类型
//一个参数Testtest=name-> {
System.out.println(name+"test");
};
test.test("小刘学编程");

5.2 方法体部分的精简

当⼀个方法体中的逻辑,有且只有⼀句的情况下,⼤括号可以省略

Testtest=name->System.out.println(name+"test");

test.test("小新");

5.3 return部分的精简

如果⼀个方法中唯⼀的⼀条语句是⼀个返回语句, 此时在省略掉大括号的同时, 也必须省略掉return。

Testtest= (a,b) ->a+b;

六、常用示例

lambda表达式是为了简化接口的实现的,在lambda表达式中,不应该出现比较复杂的逻辑。如果在lambda表达式中需要处理的逻辑比较复杂,会对程序的可读性造成非常大的影响,⼀般情况会单独的写⼀个方法。在lambda表达式中直接引用这个方法即可。

函数引用:引用⼀个已经存在的方法,使其替代lambda表达式完成接口的实现

6.1 静态方法的引用

语法:类::静态方法

注意事项:

  • 在引用的方法后面,不要添加小括号。
  • 引用的这个方法,参数(数量、类型)和返回值,必须要跟接口中定义的⼀致
classSubtraction{
publicstaticintsubtract(inta,intb ){
// 稍微复杂的逻辑:计算a和b的差值的绝对值if (a>b) {
returna-b;
        }
returnb-a;
    }
}
interfaceTestInterface{
inttest(inta,intb);
}
publicclassTestClass {
publicstaticvoidmain(String[] args) {
//实现多个参数,一个返回值的接口//对一个静态方法的引用,语法:类::静态方法TestInterfacetest=Subtraction::subtract;
System.out.println(test.test(1,2));
    }
}

6.2 非静态方法的引用

语法:方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象

注意事项:

  • 在引用的方法后⾯,不要添加小括号。
  • 引用的这个方法, 参数(数量、类型) 和 返回值, 必须要跟接口中定义的⼀致。
publicclassTest06 {
publicstaticvoidmain(String[] args) {
//对非静态方法的引用,需要使用对象来完成Test2test2=newCalculator()::calculate;
System.out.println(test2.calculate(2, 3));
    }
privatestaticclassCalculator{
publicintcalculate(inta, intb) {
returna>b?a-b : b-a;
         }
    }
}
interfaceTest2{
intcalculate(inta,intb);
}

6.3 构造方法的引用

使用场景

如果某⼀个函数式接口中定义的方法,仅仅是为了得到⼀个类的对象。此时我们就可以使用构造方法的引用,简化这个方法的实现。

语法:类名::new

注意事项:可以通过接口中的方法的参数, 区分引用不同的构造方法。

interfaceItemCreatorBlankConstruct {
ItemgetItem();
}
interfaceItemCreatorParamContruct {
ItemgetItem(intid, Stringname, doubleprice);
}
publicclassExe2 {
publicstaticvoidmain(String[] args) {
ItemCreatorBlankConstructcreator= () ->newItem();
Itemitem=creator.getItem();
ItemCreatorBlankConstructcreator2=Item::new;
Itemitem2=creator2.getItem();
ItemCreatorParamContructcreator3=Item::new;
Itemitem3=creator3.getItem(112, "小刘学编程", 135.99);
    }
}

6.4 Lambda 表达式创建线程

创建线程一般都是通过创建Thread对象,然后通过匿名内部类重写run()方法,一提到匿名内部类就应该想到可以使用 lambda 表达式来简化线程的创建过程。

Threadt=newThread(() -> {
for (inti=0; i<10; i++) {
System.out.println("新建线程->"+":"+i);
    }
});
t.start();

6.5 遍历集合

可以调用集合的 public void forEach(Consumer<? super E> action) 方法,通过 lambda 表达式的方式遍历集合中的元素。以下是Consumer接口的方法以及遍历集合的操作。Consumer接口是jdk提供的一个函数式接口。

@FunctionalInterfacepublicinterfaceConsumer<T> {
voidaccept(Tt);
//....}
ArrayList<Integer>list=newArrayList<>();
Collections.addAll(list, 1,2,3,4,5);
//lambda表达式 方法引用list.forEach(System.out::println);
list.forEach(item-> {
if (element%2==0) {
System.out.println(item);
    }
});

6.6 删除集合中的某个元素

通过public boolean removeIf(Predicate<? super E> filter)方法来删除集合中的某个元素,Predicate也是jdk为提供的一个函数式接口,可以简化程序的编写。

ArrayList<Item>items=newArrayList<>();
items.add(newItem(11, "小牙刷", 12.05 ));
items.add(newItem(5, "日本马桶盖", 999.05 ));
items.add(newItem(7, "格力空调", 888.88 ));
items.add(newItem(17, "肥皂", 2.00 ));
items.add(newItem(9, "冰箱", 4200.00 ));
items.removeIf(ele->ele.getId() ==7);
//通过 foreach 遍历,查看是否已经删除items.forEach(System.out::println);

6.7 集合内元素的排序

若要为集合内的元素排序,就必须调用sort方法,传入比较器匿名内部类重写compare 方法,现在可以使用lambda 表达式来简化代码。

ArrayList<Item>list=newArrayList<>();
list.add(newItem(13, "背心", 7.80));
list.add(newItem(11, "半袖", 37.80));
list.add(newItem(14, "风衣", 139.80));
list.add(newItem(12, "秋裤", 55.33));
list.sort((o1, o2) ->o1.getId() -o2.getId());
System.out.println(list);

七、注意

这⾥类似于局部内部类匿名内部类,依然存在闭包的问题。如果在lambda表达式中,使用到了局部变量,那么这个局部变量会被隐式的声明为 final。是⼀个常量,不能修改值。

如下代码示例:如果我们把注释放开会报错,提示num值是final不能被改变。这里虽然没有标识num类型为final,但是在编译期间虚拟机会加上fina修饰关键字。

publicstaticvoidmain(String[] args) {
intnum=10;
Test<String>test= () -> {
System.out.println(num);
    };
//num = num + 2;test.doSomeThing("hello world !");
}

总结

本文从Lambda表达式的基础概念、函数式接口、以及Lambda表达式的常用示例几方面完整的讨论了这一Java8新增的特性,实际开发中确实为我们提供了许多便利,简化了代码。欢迎小伙伴继续提出不同的见解一起讨论!

参考 & 鸣谢

1、Java中Lambda表达式使用及详解 https://blog.csdn.net/qq_45263520/article/details/123772771

2、Lambda表达式详解 https://www.cnblogs.com/haixiang/p/11029639.html

感谢前人的经验、分享和付出,让我们可以有机会站在巨人的肩膀上眺望星辰大海!

相关文章
|
2天前
|
Java
Java 正则表达式高级用法
Java 中的正则表达式是强大的文本处理工具,用于搜索、匹配、替换和分割字符串。`java.util.regex` 包提供了 `Pattern` 和 `Matcher` 类来高效处理正则表达式。本文介绍了高级用法,包括使用 `Pattern` 和 `Matcher` 进行匹配、断言(如正向和负向前瞻/后顾)、捕获组与命名组、替换操作、分割字符串、修饰符(如忽略大小写和多行模式)及 Unicode 支持。通过这些功能,可以高效地处理复杂文本数据。
|
3天前
|
Java 程序员 API
Java中的Lambda表达式:简化代码的秘密武器
在Java 8中引入的Lambda表达式是一种强大的编程工具,它可以显著简化代码,提高可读性。本文将介绍Lambda表达式的基本概念、优势以及在实际开发中的应用。通过具体示例,您将了解如何使用Lambda表达式来简化集合操作、线程编程和函数式编程。让我们一起探索这一革命性的特性,看看它是如何改变Java编程方式的。
17 4
|
3天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码
【8月更文挑战第49天】在Java 8的发布中,Lambda表达式无疑是最令人兴奋的新特性之一。它不仅为Java开发者提供了一种更加简洁、灵活的编程方式,而且还极大地提高了代码的可读性和开发效率。本文将通过实际代码示例,展示如何利用Lambda表达式优化和重构Java代码,让你的编程之旅更加轻松愉快。
|
6天前
|
Java 开发者
探索Java中的Lambda表达式:简化代码,提升效率
【9月更文挑战第14天】本文旨在揭示Java 8中引入的Lambda表达式如何革新了我们编写和管理代码的方式。通过简洁明了的语言和直观的代码示例,我们将一起走进Lambda表达式的世界,了解其基本概念、语法结构以及在实际编程中的应用。文章不仅会展示Lambda表达式的魅力所在,还会指导读者如何在日常工作中有效利用这一特性,以提高编码效率和程序可读性。
|
14天前
|
存储 Java
探索Java中的Lambda表达式
【9月更文挑战第6天】Lambda表达式是Java 8引入的一个强大特性,它允许我们将函数作为参数传递或作为返回值。在这篇文章中,我们将深入探讨Lambda表达式的概念、语法和用法,以及如何在实际项目中应用它们来简化代码。通过学习本文,你将能够更好地理解Lambda表达式的作用,并掌握如何在Java中使用它们。
|
12天前
|
并行计算 Java 开发者
探索Java中的Lambda表达式:简化代码,提升效率
Lambda表达式在Java 8中引入,旨在简化集合操作和并行计算。本文将通过浅显易懂的语言,带你了解Lambda表达式的基本概念、语法结构,并通过实例展示如何在Java项目中应用Lambda表达式来优化代码,提高开发效率。我们将一起探讨这一现代编程工具如何改变我们的Java编码方式,并思考它对程序设计哲学的影响。
|
18天前
|
安全 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法的深度融合
在Java的演进历程中,Lambda表达式无疑是Java 8引入的一项革命性特性,它极大地简化了函数式编程在Java中的应用,使得代码更加简洁、易于阅读和维护。而这一切的背后,JVM的invokedynamic指令功不可没。本文将深入探讨invokedynamic指令的工作原理及其与Java Lambda语法的紧密联系,带您领略这一技术背后的奥秘。
13 1
|
20天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码
【8月更文挑战第31天】 在Java 8的发布中,Lambda表达式无疑是最令人兴奋的新特性之一。它不仅为Java开发者提供了一种更加简洁、灵活的编程方式,而且还极大地提高了代码的可读性和开发效率。本文将通过实际代码示例,展示如何利用Lambda表达式优化和重构Java代码,让你的编程之旅更加轻松愉快。
|
Java Unix 数据库管理
java定时框架:表达式设置
Quartz中时间表达式的设置-----corn表达式 (注:这是让我看比较明白的一个博文,但是抱歉,没有找到原作者,如有侵犯,请告知)   时间格式: ,   分别对应: 秒>分>小时>日>月>周>年,  举例: 1.
863 0
|
7天前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
【Java面试题汇总】多线程、JUC、锁篇(2023版)