Java 8 新特性——lambda表达式

什么是lambda表达式?
  1. lambda 表达式是一个可传递的代码块,可以在以后执行一次或者多次 ----->《Java核心技术》
  2. lambda 表达式是一个匿名函数
为什么使用lambda表达式?

先看一个例子,这是一个典型的比较器,使用这个比较器可以对任意字符串按长度进行排序。

    Comparator<String> com = new Comparator<String>() {  
    @Override
    public int compare(String first, String second) {

        return first.length()-second.length();
    }
    };

    String[] str = {"saddsa", "sdadcsa","rgeavrr", "csfdcwe"};//如果使用Arrays类中的sort()方法,只能按照字典顺序排序。假如想要对其
                                                             //按照长度排序,需要传入比较器
    Arrays.sort(str, com);

分析这段代码,其实我们不难发现,在执行排序方法的时候。compare() 方法并非只执行一次,在数组完成排序之前,sort() 方法会一直调用compare() 方法,只要元素的顺序不正确就会重新排列元素,将比较所需的 代码块(s1.length()-s2.length()) 放在 sort() 方法中,与其他的排序逻辑集成然后完成排序。也就是是说,这里存在一个不断回调的过程。

这么一分析,我们发现,真正参与排序逻辑的只是 代码块(s1.length()-s2.length()) 罢了。也许我们会想,为什么不直接给 sort() 方法传递这个代码块,而需要给它传递一个 Comparator 实例呢?这样不会显得过于繁琐吗?实际上,在Java 8 之前,我们也只能这样去做,Java是面向对象的语言,假若我们需要传递某段代码块,必须构造一个对象,这个对象的类需要有一个方法包含所需的代码。难道就没有解决的办法了?当然不是!

Java 8 推出了 lambda 表达式,能非常完美的解决这个问题,可以使代码变得更加简洁,灵活,优雅。(这时你可以思考一下Java语言为什么美?)

废话不多说,直接上代码

      //第一种方式
     Comparator<String> com = (first, second) -> first.length() - second.length();

     String[] str = {"saddsa", "sdadcsa","rgeavrr", "csfdcwe"};

     Arrays.sort(str, com);

     //第二种方式
    Arrays.sort(str, (first, second) -> first.length() - second.length());

看到没有,看到没有,这就是差距!你那么多废话,我一句就搞定,而且我效率还比你高,这就是强!
现在,让我们一起进入 lambda表达式 的世界,请在开始之前想一想刚才我所说的:lambda表达式传递的是代码块(函数),请暂时忘掉对象

如何使用lambda表达式
  1. -> :lambda 操作符,它将 lambda表达式 分为两个部分
     左侧:指定 lambda表达式 所需要的所有参数
     右侧:lambda表达式的主体,即 lambda表达式 要执行的功能

  2. lambda表达式语法

//方式一
Runnable r1 = () -> System.out.println("Hello World");//即使没有参数,也要提供括号

//方式二
Consumer<String> fun = (args) -> System.out.println(args);//方法带参数,需要传入一个参数
Consumer<String> fun = args -> System.out.println(args);//lambda 只需要一个参数时,参数的小括号可以省略

//方式三
BinaryOperator<long> bo = (x, y) -> {  //lambda 需要两个参数,并且有返回值
            System.out.println("实现接口");
            return x + y;
};
BinaryOperator<long> bo = (long x, long y) -> x + y;//当lambda体只有一条语句时,return和大括号可以省略

BinaryOperator<long> bo = ( x,  y) -> x + y;//类型可以省略,Java可以通过上下文推断得出,称为类型推断
函数式接口——理解 lambda表达式的关键

任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
 注意:这里说函数式接口必须有一个抽象方法也许会使大家疑惑。我们会想接口中所有的方法不都是抽象的吗?
实际上,接口完全有可能重新 声明Object类的 toString, clone 等方法,这些声明有可能会让方法不再是抽象的。
不过,默认方法和静态方法不会破坏函数式接口的定义

    @FunctionalInterface//该注解会强制检查该接口是不是一个函数式接口
    public interface FunctionalDefaultMethods {
                 void method();
                 default void defaultMethod() {    //允许默认方法        
              }
        }

lambda表达式只支持函数式接口,因为lambda表达式到最后只能转换为函数式接口。其实就我个人理解而言,这是一种必要的转换,lambda表达式本来就是为了方便某个重复性的功能,使对象的概念弱化,凸显逻辑代码的地位(也就是代码块)。如果不是函数式接口,你弄他好几个方法,lambda表达式总不能一次性全部去执行吧,这在逻辑上也是行不通的。这纯属是我对函数式接口的个人理解,也许会有误,欢迎讨论。

方法引用与构造器引用

前面说过,如果 lambda表达式 的代码块只有一条代码,可以省略花括号。同样的,你也可以使用方法引用和构造器引用。使用方法引用和构造器引用可以使代码阅读性更强,更简洁。

  • 方法引用 戳一戳 1 戳一戳 2
    方法引用有四种情况(在Java核心技术中分为三种情况,这里我把构造器引用也归入方法引用之中,因为实际上构造器不也是方法嘛)
  1. object::InstanceMethodName 引用对象的实例方法
  2. ClassName::staticMethodName 引用类的静态方法(普通类)
  3. Class::instanceMethodName 引用类型对象的实例方法(比如String)
  4. ClassName::new 引用构造方法
Timer t = new Timer(1000, ele -> System.out.println(ele));//添加一个定时器,每隔1秒打印一次 ele

//使用方法引用可以写成(与上面的等价)
Timer t = new Timer(1000, System.out::println);//直接将 println 方法传递给构造器


对字符串排序
Arrays.sort(str, String::compareToIgnoreCase);//等价于 (x, y) -> x.compareToIgnoreCase(y)
                                                //类似的 Math::pow 等价于 (x, y) -> Math.pow(x, y);

假如有多个重载的同名方法,编译器会尝试从上下文找出你指的那一个方法。不知不觉,Java又强势装逼一波。
  • 构造器引用——主要与Stream结合使用:与方法引用类似,将方法名换为new。
    //有一个字符串列表,将其转换为Person对象数组,需要在字符串上调用构造器
    ArrayList<String> names = .....;
    Stream<Person> stream = names.stream().map(Person::new);//Person::new 是 Person构造器 的一个引用
    List<Person> people = stream.collect(Collection.toList);
  • 数组也可以建立构造器引用
    int[]::new 等价于 x -> new int[]

    //将流中的对象转换为对象数组
    Person[] person = stream.toArray(Person[]::new);
处理lambda表达式

通过上面的介绍,我们或许对lambda表达式有了进一步的理解,现在我们将学会怎样去使用lambda表达式。

使用lambda表达式的重点是为了延迟执行,下面是使用lambda表达式的几个场景
1.在一个单独的线程中运行代码
2.多次运行代码
3.在算法的适当位置运行代码(排序中的比较操作)
4.发生某种情况时执行代码(点击按钮数据到达)

其实,我们可以想一想,这些情况大多在哪里出现呢?想到排序,想到比较,这很容易让我们联想到集合和数组。嗯,就我现阶段的水平而言,或许我只能理解lambda表达式在集合和数组中给我带来的便利,但我想这也就足够了。

    //这就是一个比较常规的用法,可以把lambda表达式作为参数传递给方法,而且需要注意,lambda表达式的参数必须是一个函数式接口
    public static void repeat(int n , Runnable action){
        for(int i = 0 ; i < n; i++)
        action.run();
    }

    repeat(10, () -> System.out.println("Hello, world"));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值