java8新特性(二):Lambda表达式

一、什么是Lambda表达式

    上一篇中已经看到了Lambda表达式的使用场景,这一篇深入学习一下Java8的这个新特性。

     首先Lambda说一下表达式的定义:它是一个简洁的可以用于传递的匿名函数。 包含以下一些特性:

     1- 它是匿名的: 它不像不通的方法那样有一个明确的名称。

     2- 它是一个函数: 因为它不属于某个特定的类,但是它有参数列表、函数体、返回类型、和异常列表。

     3- 它可以被传递: 可以当成参数传递给一个方法,或者存储在一个变量当中。

     4- 它是简洁的:不需要像匿名类那样写很多冗余的代码。

 

 

Lambda表达式的结构:

       

java8新特性(二):Lambda表达式

 

没有Lambda表达式 之前写一个匿名类是这样写的:

Comparator<Apple> byWeight = new Comparator<Apple>() {

public int compare(Apple a1, Apple a2){

     return a1.getWeight().compareTo(a2.getWeight());

}

};

有了Lambda表达式 之后:

Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

 

Lambda表达式的基本语法有两种形式

一种表达式语法,不带花挂号 :(parameters) -> expression

表达式最后一句就是返回值,不需要显示的return语句。

或者

一种是语句语法,带有花挂号:(parameters) -> { statements; }

带花挂号的必须是完整的语句,不能写表达式。

 

例子

用例 lambda表达式实例
布尔表达式 (List<String> list) -> list.isEmpty()
创建对象 () -> new Apple(10)
消费一个对象 (Apple a) -> { System.out.println(a.getWeight()); }
抽取对象属性 (String s) -> s.length()
合并两个值 (int a, int b) -> a * b
比较两个对象 (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

 

二、Lambda表达式的使用场景

你可以在函数式接口上下文的环境中使用Lambda表达式。

 

那么什么是函数式接口呢? 简要的说函数式接口就是有且只有一个抽象方法的接口.

 

 

 

 

三、函数描述器

定义:函数描述器是函数接口的抽象方法的方法签名,从本质上描述Lambda表达式的签名,我们称这种抽象方法为函数描述器。

举例来说,Runnable接口就可以被描述为一个函数接口的签名,它没有参数没有返回值。

 

Java 8 中通用的函数没描述器

函数接口 函数描述器 基本类型函数接口
Predicate<T> T -> boolean IntPredicate, LongPredicate, DoublePredicate
Consumer<T> T -> void IntConsumer, LongConsumer, DoubleConsumer
Function<T, R> T -> R IntFunction<R>, IntToDoubleFunction, IntToLongFunction, LongFunction<R>, LongToDoubleFunction, LongToIntFunction, DoubleFunction<R>, ToIntFunction<T>, ToDoubleFunction<T>, ToLongFunction<T>
Supplier<T> () -> T BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier
UnaryOperator<T> T -> T IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator
BinaryOperator<T> (T, T) -> T IntBinaryOperator, LongBinaryOperator, DoubleBinaryOperator
BiPredicate<L, R> (L, R) -> boolean  
BiConsumer<T, U> (T, U) -> void ObjIntConsumer<T>, ObjLongConsumer<T>, ObjDoubleConsumer<T>
BiFunction<T, U, R> (T, U) -> R ToIntBiFunction<T, U>, ToLongBiFunction<T, U>, ToDoubleBiFunction<T, U>

 

由于java 中基本类型和包装类型 之间可以自动实现相互转化,叫做boxing 和unboxing。

但是这个过程会带来性能的开销。所以在java8 中提供了基本类型的功能接口。

 

 

四、类型检查、类型推断、约束

 

Lambda表达式没有明确的类型说明, 它的类型会根据它所在的上下文推断。

 

 

Figure 3.4. Deconstructing the type-checking process of a lambda expression

 

 

 

相同的Lambda表达式,不同的功能接口。

 

 

Callable<Integer> c = () -> 42;

PrivilegedAction<Integer> p = () -> 42;

 

Comparator<Apple> c1 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

 

ToIntBiFunction<Apple, Apple> c2 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

 

BiFunction<Apple, Apple, Integer> c3 = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

 

void 返回值可以兼容

// Predicate has a boolean return

Predicate<String> p = s -> list.add(s);

// Consumer has a void return

Consumer<String> b = s -> list.add(s);

 

类型推断

java8新特性(二):Lambda表达式

 

约束

lambda表达式可以 没有约束的捕获实例变量和静态变量,但是捕获局部变量时,被捕获的局部变量必须是final类型的或者effectively final的 (定义为final 或者  事实上的final类型),并且lambda表达式不能改变局部变量的值。

 

 

五、方法引用

方法引用可以让你重复使用现有的方法定义,并且像Lambda表达式一样传递他们,而且比lambda表达式更加简洁。

 

//引用普通方法

Converter<String, Integer> converter = Integer::valueOf;

 

//引用构造方法

PersonFactory<Person> personFactory = Person::new;

 

 

六、 Lambda表达式访问变量的作用域

 

 

public  void testLambdaScope(){
    //访问局部变量
    int num = 1;
    Converter<Integer, String> stringConverter =  (from) -> String.valueOf(from + num);
    String converted  = stringConverter.convert(2);     // 3
    System.out.println(converted);   // 3

    //访问成员变量
    Converter<Integer, String> stringConverter1 = (from) -> {
        outerNum = 23;
        return String.valueOf(from);
    };
    //访问静态变量
    Converter<Integer, String> stringConverter2 = (from) -> {
        outerStaticNum = 72;
        return String.valueOf(from);
    };


    //访问接口的 默认方法 是不行的
    Formula formula = (a) -> sqrt( a * 100);

}