Lambda表达式

概述

Lambda 表达式也可称为闭包,是推动 Java 8 发布的最重要新特性。lambda表达式本质上是一个匿名方法Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中)或者把代码看成数据。使用 Lambda 表达式可以使代码变的更加简洁紧凑。在最简单的形式中,一个lambda可以由:用逗号分隔的参数列表、–>符号、函数体三部分表示,在某些情况下lambda的函数体会更加复杂,这时可以把函数体放到在一对花括号中,就像在Java中定义普通函数一样。Lambda可以引用类的成员变量与局部变量(如果这些变量不是final的话,它们会被隐含的转为final,这样效率更高)。Lambda可能会返回一个值。返回值的类型也是由编译器推测出来的。如果lambda的函数体只有一行的话,那么没有必要显式使用return语句。
如何使现有的函数友好地支持lambda。最终采取的方法是:增加函数式接口的概念。函数式接口就是接口里面必须有且只有一个抽象方法的普通接口,像这样的接口可以被隐式转换为lambda表达式成为函数式接口。 在可以使用lambda表达式的地方,方法声明时必须包含一个函数式的接口。 任何函数式接口都可以使用lambda表达式替换,例如:ActionListener、Comparator、Runnable。
函数式接口是容易出错的:如有某个人在接口定义中增加了另一个方法,这时,这个接口就不再是函数式的了,并且编译过程也会失败。为了克服函数式接口的这种脆弱性并且能够明确声明接口作为函数式接口的意图,Java 8增加了一种特殊的注解@FunctionalInterface,但是默认方法与静态方法并不影响函数式接口的契约,可以任意使用。
使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例。通过() -> {}代码块替代了整个匿名类。
java 8之前:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(“Before Java8, too much code for too little to do”);
}
}).start();
Java 8方式:
new Thread( () -> System.out.println(“In Java8, Lambda expression rocks !!”) ).start();
Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。

这个例子向我们展示了 Java 8 lambda 表达式的语法。你可以使用 lambda 写出如下代码:
(params) -> expression
(params) -> statement
(params) -> { statements }
例如,如果你的方法不对参数进行修改、重写,只是在控制台打印点东西的话,那么可以这样写:
() -> System.out.println(“Hello Lambda Expressions”);

要点 1:lambda 表达式的使用位置
预定义使用了 @Functional 注释的函数式接口,自带一个抽象函数的方法,或者 SAM(Single Abstract Method单个抽象方法)类型。这些称为 lambda 表达式的目标类型,可以用作返回类型,或 lambda 目标代码的参数。例如,若一个方法接收 Runnable、Comparable 或者 Callable 接口,都有单个抽象方法,可以传入 lambda 表达式。类似的,如果一个方法接受声明于 java.util.function 包内的接口,例如 Predicate、Function、Consumer 或 Supplier,那么可以向其传 lambda 表达式。
要点 2:lambda 表达式和方法引用
lambda 表达式内可以使用方法引用,仅当该方法不修改 lambda 表达式提供的参数。本例中的 lambda 表达式
可以换为方法引用,因为这仅是一个参数相同的简单方法调用。
list.forEach(n -> System.out.println(n));
list.forEach(System.out::println); // 使用方法引用
然而,若对参数有任何修改,则不能使用方法引用,而需键入完整地 lambda 表达式,如下所示:
list.forEach((String s) -> System.out.println(“" + s + "”));
事实上,可以省略这里的 lambda 参数的类型声明,编译器可以从列表的类属性推测出来。
要点 3:lambda 表达式内部引用资源
lambda 内部可以使用静态、非静态和局部变量,这称为 lambda 内的变量捕获。
要点 4:lambda 表达式也成闭包
Lambda 表达式在 Java 中又称为闭包或匿名函数,所以如果有同事把它叫闭包的时候,不用惊讶。
要点 5:lambda 表达式的编译方式
Lambda 方法在编译器内部被翻译成私有方法,并派发 invokedynamic 字节码指令来进行调用。可以使用 JDK
中的 javap 工具来反编译 class 文件。使用 javap -p 或 javap -c -v 命令来看一看 lambda 表达式生成的字节码。
大致应该长这样:
private static java.lang.Object lambda$0(java.lang.String);
要点 6:lambda 表达式的限制
lambda 表达式有个限制,那就是只能引用 final 或 final 局部变量,这就是说不能在 lambda 内部修改定义在
域外的变量。
List primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { factor++; });
Error:
Compile time error : “local variables referenced from a lambda expression must be final or
effectively final”
另外,只是访问它而不作修改是可以的,如下所示:
List primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { System.out.println(factor*element); });
输出:
4
6
10
14
因此,它看起来更像不可变闭包,类似于 Python。

Lambda 表达式优缺点?

lambda 表达式,也被称为闭包,它是推动 Java 8 发布的最重要新特性。lambda 允许把函
数作为一个方法的参数(函数作为参数传递进方法中),使用 Lambda 表达式可以使代码变
的更加简洁紧凑。
优点:
代码更加简洁
减少匿名内部类的创建,节省资源
使用时不用去记忆所使用的接口和抽象函数
缺点:
不易于后期维护,必须熟悉 lambda 表达式和抽象函数中参数的类型
可读性差
若不用并行计算,很多时候计算速度没有比传统的 for 循环快。(并行计算有时需要预热
才显示出效率优势)
不容易调试。
若其他程序员没有学过 lambda 表达式,代码不容易让其他语言的程序员看懂。

Lambda 的表达式作用域(Lambda Scopes)

访问局部变量
我们可以直接在 lambda 表达式中访问外部的局部变量:但是和匿名对象不同的是,这里
的变量可以不用声明为 final,该代码同样正确,不过这里的变量必须不可被后面的代码修 改(即隐性的具有 final 的语义)
访问字段和静态变量
与局部变量相比,我们对 lambda 表达式中的实例字段和静态变量都有读写访问权限。 该行为和匿名对象是一致的。
访问默认接口方法
无法从 lambda 表达式中访问默认方法

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

package com.chen.test.JAVA8Features;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Demo01 {
    private static Logger log = LoggerFactory.getLogger(Demo01.class);

    public static void main(String[] args) {
        Runnable t1 =new Runnable(){
            @Override
            public void run(){
                log.info("我是没有使用Lambda表达式:不简洁");
            }
        };
        
        Runnable t2 = () -> log.info("我是使用Lambda表达式:简洁、灵活");
        
        t1.run();
        t2.run();
        
    }
}

Lambda表达式语法

Lambda表达式在java语言中引入了一种新的语法元素和操作。这种操作符号为“->”,Lambda操作符或箭头操作符,它将Lambda表达式分割为两部分。
左边:指Lambda表达式的所有参数
右边:指Lambda体,即表示Lambda表达式需要执行的功能。
六种语法格式:
1、语法格式一:无参数、无返回值,只需要一个Lambda体
package com.chen.test.JAVA8Features;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Demo02 {
private static Logger log = LoggerFactory.getLogger(Demo02.class);

public static void main(String[] args) {
Runnable t1 = ()-> log.info(“Lambda表达式:简洁、灵活,优雅永不过时”);
t1.run();
}
}
22:22:39.125 [main] INFO com.chen.test.JAVA8Features.Demo02 - Lambda表达式:简洁、灵活,优雅永不过时
2、语法格式二:lambda有一个参数、无返回值
package com.chen.test.JAVA8Features;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Consumer;
public class Demo03 {
private static Logger log = LoggerFactory.getLogger(Demo03.class);
public static void main(String[] args) {
Consumer consumer = new Consumer() {
@Override
public void accept(String s) {
log.info(s);
}
};
consumer.accept(“爱与被爱的区别”);

    Consumer<String> consumer1 = (s) -> log.info(s);
    consumer1.accept("接受爱不一定爱对方,爱一定付出真心爱");
}

}

23:03:08.992 [main] INFO com.chen.test.JAVA8Features.Demo03 - 爱与被爱的区别
23:03:09.142 [main] INFO com.chen.test.JAVA8Features.Demo03 - 接受爱不一定爱对方,爱一定付出真心爱
3、语法格式三:Lambda只有一个参数时,可以省略()
package com.chen.test.JAVA8Features;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.function.Consumer;
public class Demo04 {
private static Logger log = LoggerFactory.getLogger(Demo04.class);
public static void main(String[] args) {
Consumer consumer = s -> log.info(s);
consumer.accept(“Lambda只有一个参数时,可以省略()”);
}
}
package com.chen.test.JAVA8Features;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Comparator;
public class Demo05 {
private static Logger log = LoggerFactory.getLogger(Demo05.class);

public static void main(String[] args) {
    CompareOldMethod(12,10);
    findMaxValue(12,10);
    findMinValue(12,10);
}

// 没有使用Lambda表达式比较大小
public static void CompareOldMethod(int num1,int num2){
Comparator comparator = new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
log.info(“o1:{}”,o1);
log.info(“o2:{}”,o2);
return o1 < o2 ? o2 : o1;
}
};
log.info(“OldFindMaxValue:{}”,comparator.compare(num1,num2));
}

// 使用lambda表达式
public static void findMaxValue(int num1,int num2){
Comparator comparatorMax = (o1, o2) ->{
log.info(“o1:{}”,o1);
log.info(“o2:{}”,o2);
return (o1<o2)? o2 :(o1);
};
log.info(“findMaxValue:{}”,(comparatorMax.compare(num1,num2)));

}
public static void findMinValue(int num1,int num2){
    Comparator<Integer> comparatorMin =  (o1, o2) -> {
        log.info("o1:{}",o1);
        log.info("o2:{}",o2);
        return (o1 < o2) ? o1 : o2;
    };
    log.info("FindMinValue:{}",comparatorMin.compare(num1,num2));
}

}

00:17:10.206 [main] INFO com.chen.test.JAVA8Features.Demo05 - o1:12
00:17:10.206 [main] INFO com.chen.test.JAVA8Features.Demo05 - o2:10
00:17:10.206 [main] INFO com.chen.test.JAVA8Features.Demo05 - OldFindMaxValue:12
00:17:10.315 [main] INFO com.chen.test.JAVA8Features.Demo05 - o1:12
00:17:10.315 [main] INFO com.chen.test.JAVA8Features.Demo05 - o2:10
00:17:10.315 [main] INFO com.chen.test.JAVA8Features.Demo05 - findMaxValue:12
00:17:10.315 [main] INFO com.chen.test.JAVA8Features.Demo05 - o1:12
00:17:10.315 [main] INFO com.chen.test.JAVA8Features.Demo05 - o2:10
00:17:10.315 [main] INFO com.chen.test.JAVA8Features.Demo05 - FindMinValue:10
Process finished with exit code 0
5、语法格式五:当Lambda体只有一条语句的时候,return和{}可以省略掉
package com.chen.test.JAVA8Features;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Comparator;

public class Demo05 {
private static Logger log = LoggerFactory.getLogger(Demo05.class);

public static void main(String[] args) {
    findMaxValue(12,10);
    findMinValue(12,10);
}

// 使用lambda表达式
public static void findMaxValue(int num1,int num2){
Comparator comparatorMax = (o1, o2) ->{

        log.info("o1:{}",o1);
        log.info("o2:{}",o2);
        return (o1<o2)? o2 :(o1);
    };
    log.info("findMaxValue:{}",(comparatorMax.compare(num1,num2)));
}
public static void findMinValue(int num1,int num2){
    Comparator<Integer> comparatorMin =  (o1, o2) -> (o1 < o2) ? o1 : o2;

    log.info("FindMinValue:{}",comparatorMin.compare(num1,num2));
}

}

6、语法格式六:类型推断:数据类型可以省略,因为编译器可以推断得出,成为“类型推断”

package com.chen.test.JAVA8Features;
import com.mysql.cj.callback.MysqlCallbackHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;
public class Demo07 {
    private static Logger log = LoggerFactory.getLogger(Demo07.class);
    public static void main(String[] args) {
        dateType();
    }

    public static void dateType(){
        Consumer<String> consumer = (String s) -> log.info(s);
        consumer.accept("Hello World !");
        Consumer<String> consumer1 = (s) -> log.info(s);
        consumer1.accept("Hello don't date type !");
    }
}
  • 15
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

思静语

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

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

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

打赏作者

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

抵扣说明:

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

余额充值