java8新特性带你入门

它是什么

术语定义

百度百科定义: “Lambda 表达式”(lambda expression)是一个==匿名函数==,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包

java中的实现方式

lambda表达式的目标类型是“函数接口(functional interface)”,这是Java8新引入的概念。它的定义是: 一个接口,如果只有一个显式声明的抽象方法,那么它就是一个函数接口。一般用@FunctionalInterface标注出来(也可以不标)。

Lambda表达式 vs 匿名类

既然lambda表达式即将正式取代Java代码中的匿名内部类,那么有必要对二者做一个比较分析。一个关键的不同点就是关键字 this。匿名类的 this 关键字指向匿名类,而lambda表达式的 this 关键字指向包围lambda表达式的类。另一个不同点是二者的编译方式。Java编译器将lambda表达式编译成类的私有方法。使用了Java 7的 invokedynamic 字节码指令来动态绑定这个方法。

同类技术:

Python, Ruby, Lua

为什么会出现

  1. 简化代码
  2. 有些运算中的变量甚至是算法函数过程其实不是必须要定义出来,只是中间过渡一下
  3. 对之后stream接口集合的便利操作提供基础支持,lambda 表达式能简化集合上数据的多线程或者多核的处理,提供更快的集合处理速度

实例走一波

1. 线程创建

//lambda之前
new Thread(new Runnable(){
    @Override
    public void run(){
        System.out.println("我在努力跑");
    }
}).start();

//lambda之后
new Thread(() -> System.out.println("我起跑更快")).start();

2. Predicate 条件函数式接口,并且可以多个条件过滤

Predicate startsWithJ = (n) -> n.startsWith("J");

Predicate fourLetterLong = (n) -> n.length() ==4;

names.stream().filter(startsWithJ.and(fourLetterLong))

.forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : "+ n));

3. stream 接口

  • 3.1 map 操作

    本例介绍最广为人知的函数式编程概念map。它允许你将对象进行转换。例如在本例中,我们将 costBeforeTax 列表的每个元素转换成为税后的值。我们将 x -> x*x lambda表达式传到 map() 方法,后者将其应用到流中的每一个元素。然后用 forEach() 将列表元素打印出来。

// 不使用lambda表达式为每个订单加上12%的税
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
    double price = cost + .12*cost;
    System.out.println(price);
}
 
// 使用lambda表达式
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost) -> cost + .12*cost).forEach(System.out::println);
  • 3.2 reduce操作

    在上个例子中,可以看到map将集合类(例如列表)元素进行转换的。还有一个 reduce() 函数可以将所有值合并成一个。Map和Reduce操作是函数式编程的核心操作,因为其功能,reduce 又被称为折叠操作。另外,reduce 并不是一个新的操作,你有可能已经在使用它。SQL中类似 sum()、avg() 或者 count() 的聚集函数,实际上就是 reduce 操作,因为它们接收多个值并返回一个值。流API定义的 reduceh() 函数可以接受lambda表达式,并对所有值进行合并。IntStream这样的类有类似 average()、count()、sum() 的内建方法来做 reduce 操作,也有mapToLong()、mapToDouble() 方法来做转换。这并不会限制你,你可以用内建方法,也可以自己定义。在这个Java 8的Map Reduce示例里,我们首先对所有价格应用 12% 的VAT,然后用 reduce() 方法计算总和。

// 为每个订单加上12%的税
// 老方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
    double price = cost + .12*cost;
    total = total + price;
}
System.out.println("Total : " + total);
 
// 新方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println("Total : " + bill);
4. 匿名方法改进之路(Lambda 和方法引用实战)
  • 第1种:传递代码

    Java 8的API已经为你提供了一个 List 可用的 sort 方法,那么如何把排序策略传递给 sort 方法呢?sort方法的签名是这样的:

    void sort(Comparator<? super E> c)
    它需要一个 Comparator 对象来比较两个Apple!这就是在Java中传递策略的方式:它们必须包裹在一个对象里。我们说 sort 的行为被参数化了:传递给它的排序策略不同,其行为也会 不同。
    第一个解决方案可以是这样的:

    public class AppleComparator implements Comparator<Apple> {
    
            @Override
            public int compare(Apple o1, Apple o2) {
                return o1.getWeight().compareTo(o2.getWeight());
            }
    }
    
    apples.sort(new AppleComparator())
    
  • 第2步:使用匿名类

    可以使用匿名类来改进方案,而不是实现一个 Comparator 却只实例化一次:

    apples.sort(new Comparator<Apple>() {
      @Override
      public int compare(Apple o1, Apple o2) {
          return o1.getWeight().compareTo(o2.getWeight());
      }
    });
    
  • 第3步:使用 Lambda 表达式

    接下来使用 Lambda 表达式来改进方案:

    apples.sort((Apple a1,Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
    

    Comparator 具有一个叫作 comparing 的静态辅助方法,它可以接受一个 Function 来提取 Comparable 键值,并生成一个 Comparator 对象,它可以像下面这样用(注意你现在传递的Lambda只有一 个参数:Lambda说明了如何从苹果中提取需要比较的键值):

    apples.sort(Comparator.comparing(((Apple apple) -> apple.getWeight())));
    
  • 第4步:使用方法引用

    方法引用就是替代那些转发参数的 Lambda 表达式的语法糖。可以用方法引 用改进方案如下:

    apples.sort(Comparator.comparing(Apple::getWeight));
    

结束语

如上所述,lambda表达式等新特性给我们编程实现带来了新的支持可能,更加方便的计算(尤其是集合)

  • Lambda表达式可以理解为一种匿名函数:它没有名称,但有参数列表、函数主体、返回 类型,可能还有一个可以抛出的异常的列表。

  • Lambda表达式让你可以简洁地传递代码。

  • 函数式接口就是仅仅声明了一个抽象方法的接口。

  • 只有在接受函数式接口的地方才可以使用Lambda表达式。

  • Lambda表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。

  • Java 8自带一些常用的函数式接口,放在java.util.function包里,包括Predicate<T>、Function<T,R>、Supplier<T>、Consumer<T>和BinaryOperator<T>。

  • 为了避免装箱操作,对Predicate<T>和Function<T, R>等通用函数式接口的原始类型特化:IntPredicate、IntToLongFunction等。

  • 环绕执行模式(即在方法所必需的代码中间,你需要执行点儿什么操作,比如资源分配 和清理)可以配合 Lambda 提高灵活性和可重用性。

  • Lambda 表达式所需要代表的类型称为目标类型。

  • 方法引用让你重复使用现有的方法实现并直接传递它们。

  • Comparator、Predicate和Function等函数式接口都有几个可以用来结合 Lambda 表达式的默认方法。

参考文献

  1. 理解java8 lambda http://www.jianshu.com/p/f569c090ec15
  2. Java8 lambda表达式10个示例
  3. java8 新特性
  4. 深入理解Java 8 Lambda表达式(Oracle官方文档版)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值