1、Lambda表达式的概念
lambda表达式是Java8的一个新特性,也是最值得学的新特性之一。
lambda表达式从本质上讲,是一个匿名函数,可以使用这个匿名函数,实现接口中的方法,对接口进行简单的实现,从而简化代码。在写lambda表达式的时候,不需要关心方法名是什么!
2.Lambda表达式的使用场景
例如:设计接口的实现类、使用匿名内部类,但是lambda表达式比这两种方式都简单。
lamnda表达式通常是用来简化接口实现。
但注意了不是所有的接口都可以使用lambda表达式去简介实现的
Lambda表达式对接口的要求:当实现的接口中方法过躲或者过少的时候,lambda表达式都是不使用的。
lambda表达式只能简化实现函数式接口(注:函数式接口:一个接口中,要求实现类必须实现的抽象方法,有且只有一个!)
3.lambda表达式的基础语法
在写lambda表达式的时候,不需要关心方法名,也不需要关心返回值类型,只需要关注两部分内容即可,参数列表和方法体。
(参数1,参数2,..参数列表) -> {
方法体
};
参数列表:方法的参数列表,要求和实现的接口中的方法参数部分一致,包括参数的数量和类型。
方法体部分:方法的实现部分,如果接口中定义的方法有返回值,则在实现的时候,注意返回值的返回,没有就不返回。
**->:**分割参数部分和方法体部分。
简单代码实现:
我们先写了四个函数式接口:
public interface HasReturnMulitParameter {
/**
* 测试 多参数有返回
* @param a
* @param b
* @return int
*/
int test(int a,int b);
}
public interface NonReturnMulitParameter {
/**
* 测试 无返回多参数
* @param a
* @param b
*/
void test(int a,int b);
}
public interface NonReturnNonParameter {
/**
* 测试 无参数无返回
*/
void test();
}
public interface NonReturnSingleParameter {
/**
* 测试 一个参数,无返回
* @param a
*/
void test(int a);
}
Test实现类:
import Lambda.TestInterFace.HasReturnMulitParameter;
import Lambda.TestInterFace.NonReturnMulitParameter;
import Lambda.TestInterFace.NonReturnNonParameter;
import Lambda.TestInterFace.NonReturnSingleParameter;
/**
* @program: jvmDemo
* @description
* @author: 不会编程的派大星
* @create: 2021-05-04 14:53
**/
public class LambdaBasicSynTest {
public static void main(String[] args) {
NonReturnNonParameter test1 = () -> {
System.out.println("我是没有返回也没有参数的那一个!");
};
NonReturnSingleParameter test2 = (int a) -> {
System.out.println("我是无返回,但有一个参数的那个! 参数: "+ a);
};
NonReturnMulitParameter test3 = (int a,int b) -> {
System.out.println("我是无返回结果但有很多参数的那一个!参数为 : "+a+" "+b);
};
HasReturnMulitParameter test4 = (int a,int b) -> {
System.out.println("我是有参数有返回结果的那一个");
return a+b;
};
test1.test();
test2.test(10);
test3.test(2,4);
System.out.println(test4.test(12, 6));
}
}
执行结果:
这里lambda表达式还可以更加精简。继续拿上面的例子来说:
public class LambdaBasicSynTest1 {
public static void main(String[] args) {
NonReturnSingleParameter test1 = a -> System.out.println(a);
HasReturnMulitParameter test2 = (a,b) -> a+b;
test1.test(10);
System.out.println(test2.test(12, 6));
}
}
执行结果:
Lambda表达式精简:
1.参数精简:
如果只有一个参数,则课省略参数列表的括号不写,多个的话剧必须要写
也可以省略不写参数的类型,以为在接口定义中,已经定义了其类型,多个参数的时候,要么都不写,要么都写
2.方法体精简:
如果整个方法体只有一条语句,则可以不用写方法体的{ },如果这单条语句还是返回语句的话,还可以省略return关键字。
4.函数引用
lambda表达式是为了简化接口的实现的。在lambda表达式中, 不应该出现比较复杂的逻辑。如果在lambda表达式中出现了过于复杂的逻辑,会对程序的可读性造成非常大的影响。如 果在lambda表达式中需要处理的逻辑比较复杂,一般情况会单独的写一个方法。在lambda表达式中 直接引用这个方法即可。
或者,在有些情况下,我们需要在lambda表达式中实现的逻辑,在另外一个地方已经写好了。此时我们就不需要再单独写一遍,只需要直接引用这个
已经存在的方法即可。
函数引用:引用一个已经存在的方法,使其替代lambda表达式完成接口的实现。
简单代码实现:
测试接口:
public interface TestInterface {
/**
* 计算
* @param a
* @param b
* @return
*/
int calculate(int a,int b);
}
测试类:
import Lambda.TestInterFace.*;
/**
* @program: jvmDemo
* @description
* @author: 不会编程的派大星
* @create: 2021-05-04 14:53
**/
public class LambdaBasicSynTest2 {
public static void main(String[] args) {
/**
* 静态方法应用,参数列表和返回值类型必须一样
*/
TestInterface testInterface = LambdaBasicSynTest2::calculate;
System.out.println(testInterface.calculate(10, 10));
}
public static int calculate(int a,int b){
if(a < b){
return b-a;
}else if(a > b){
return a-b;
}
return a+b;
}
}
执行结果:
以上这个就是lambda表达式对静态方法的引用,其基本语法为 : 类::静态方法(要求,引用的该方法的参数列表和返回值类型必须与定义的接口的参数类表和返回值类型保持一致)
接下来我们来看一看lambda表达式对非静态方法的引用:
其基本语法为 : 对象::非静态方法
import Lambda.TestInterFace.*;
/**
* @program: jvmDemo
* @description
* @author: 不会编程的派大星
* @create: 2021-05-04 14:53
**/
public class LambdaBasicSynTest2 {
public static void main(String[] args) {
/**
* 静态方法应用,参数列表和返回值类型必须一样
*/
TestInterface testInterface = new LambdaBasicSynTest2()::calculate1;
System.out.println(testInterface.calculate(10, 20));
}
public int calculate1(int a,int b){
if(a == b){
return a+b;
}
return a*b;
}
}
执行结果:
我们再来看看lambda表达式对构造方法的引用:
基本语法:对象 ::new
/**
* @program: jvmDemo
* @description
* @author: 不会编程的派大星
* @create: 2021-05-04 14:53
**/
public class LambdaBasicSynTest3 {
private static class Person{
String name;
int age;
public Person(){
System.out.println("我是无参构造器");
}
public Person(String name) {
this.name = name;
System.out.println("我是有一个参数的构造器");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("我是有两个参数的构造器");
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@FunctionalInterface
private interface Inter1{
Person get();
}
@FunctionalInterface
private interface Inter2{
Person get(String name);
}
@FunctionalInterface
private interface Inter3{
Person get(String name,int age);
}
public static void main(String[] args) {
/**
* 空参数
*/
Inter1 inter1 = Person::new;
System.out.println(inter1.get().getName());
/**
* 有一个参数的构造器
*/
Inter2 inter2 = Person::new;
System.out.println(inter2.get("Tom").getName());
/**
* 有两个参数的构造器
*/
Inter3 inter3 = Person::new;
System.out.println(inter3.get("Jack", 11).getName());
System.out.println(inter3.get("Jack", 11).getAge());
}
}
执行结果:
了解完Lambda表达式对构造方法的引用后,我们再来看看对象方法的特殊引用吧
5.对象方法的特殊引用
如果在使用lambda表达式,实现某些接口的时候。lambda表达式中包含 了某一个对象,此时方法体中,直接使用这个对象调用它的某一个方法就可以完成整体的逻辑。其他的参数,可以作为调用方法的参数。此时,可以对这种实现进行简化。.
简单代码实现:
/**
* @program: jvmDemo
* @description
* @author: 不会编程的派大星
* @create: 2021-05-04 14:53
**/
public class LambdaBasicSynTest4 {
private static class Person{
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@FunctionalInterface
private interface Test1{
String getName(Person person);
}
@FunctionalInterface
private interface Test2{
void setName(Person person,String name);
}
public static void main(String[] args) {
Person person = new Person();
person.setName("Jack");
Test1 test1 = x -> x.getName();
System.out.println(test1.getName(person));
Test1 test2 = Person::getName;
System.out.println(test2.getName(person));
Test2 test3 = (p,n) -> p.setName(n);
test3.setName(person1,"lucas");
System.out.println(person1.getName());
Test2 test4 = Person::setName;
test4.setName(person1,"hello");
System.out.println(person1.getName());
}
}
执行结果:
注:当接口方法中参数为一个类的时候,其他的参数,可以作为调用方法的参数时,可以直接使用 类::方法,
6.Lambda表达式注意点
如果留在方法中引用了局部变量,这么这个变量就会默认为final形的,但是全局变量没问题。
Lambda 可以没有限制地引用 实例变量和静态变量。但 局部变量必须显式声明为final,或事实上是final 。换句话说, Lambda 表达式只能引用指派给它们的局部变量一次。(注: 实例变量可以被看作捕获最终局部变量this 。
原因:
第一,实例变量和局部变量背后的实现有一个关键不同。 实例变量都存储在堆中,而局部变量则保存在栈上。 如果Lambda 可以直接访问局部变量,而且Lambda 是在一个线程中使用的,则使用 Lambda 的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此,Java 在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。 实例变量可以的原因是,因为它们保存在堆中,而堆是在线程之间共享的。
第二,这一限制不鼓励你使用改变外部变量的典型命令式编程模式。
这次的讨论就到这里了,欢迎小伙伴们留言讨论!