Java8新特性总结 - 3. Lambda表达式

所有示例代码打包下载 : 点击打开链接

Java8新特性 : 
  1. 接口新增默认方法和静态方法
  2. Optional类
  3. Lambda表达式
  4. 方法引用
  5. Stream API - 函数式操作流元素集合
  6. Date/Time API
  7. 新API和工具
  8. Nashorn , JavaScript引擎

3. Lambda表达式

    Lambda表达式 , (函数式编程 , 也称为闭包)是Java8中最大和最令人期待的语言改变 . 它允许我们将函数当成参数传递给某个方法 , 或者把代码本身当作数据处理(函数式开发者非常熟悉这些概念 , 例如js中的将function本身当作一个参数传入另一个function中) . Lambda相当于JDK8之前的匿名内部类 , 实现了更简洁而紧凑的语言结构 , 最简单的Lambda表达式可由参数 , -> 符号和语句块组成 . 例如 : 

Arrays.asList(1,2,3).forEach(i -> System.out.print(i));
完整的Lambda表达式由3部分组成 : 参数列表 , 箭头符号 , 代码内容
(Type1 param1 , Type2 param2 , ...) -> {statment1;statment2;return result;}
例如 : 
map.forEach((String k , String v) -> {
    System.out.println(k + " , " + v);
});
Lambda的特征 : 
    Lambda有以下重要特征 , 这些特征可以在写法上使Lambda写法更精简
    1 . 可选类型声明 , 不需要声明参数类型 , 编译器可以统一识别参数值 . 绝大多数情况 , 编译器都可以从上下文环境中推断出Lambda表达式的参数类型 , 所以参数类型可以省略
(param1 , param2 , ...) -> {statment1;statment2;return result;}
    2 . 可选的参数圆括号 , 如果只有一个参数无需定义圆括号 , 多个参数必须定义 . 
param1 -> {statment1;statment2;return result;}
    3. 可选的大括号 , 如果主体仅有一行语句 , 就不需要大括号和行尾分号
param1 -> statment1
    4 . 可选的返回关键字 , 如果主体只有一个表达式返回值 , 则编译器会自动返回值 , 可以省略return关键字 , 但此时花括号是必须的 , 以表明表达式返回一个值(经实测 , 此时花括号也可以省略 , 新版更新了?)
param1 -> {result}

注 : 在某些情况下 , 参数和符号也可省略

Arrays.asList(1 ,2).forEach(i -> System.out.println(i)); ===> Arrays.asList(1 ,2).forEach(System.out::println);
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) ); ===> Arrays.asList( "a", "b", "d" ).sort(String::compareTo);
在实际中使用Lambda表达式时 , 需要注意以下问题 : 
    1 . 在Lambda表达式中使用外部变量时 , 该变量必须是final型的(等同于之前的匿名内部类 , 只能使用外部类final修饰的变量) , 但在Java8中 , 你没必要显式的声明为变量加上final修饰符 , 编译器则默认你使用的变量为final的

    2 . Lambda和内部类的区别 : 除了精简代码外 , Lambda中如果引用this , 那么this指针是包装类 , 即Lambda被调用的类 ; 而内部类中 , 则是指内部类 .

Q : 为什么外部变量必须有final的限制 ? 
    1 . 实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此, Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。
    2 . 这一限制不鼓励你使用改变外部变量的典型命令式编程模式
函数式接口
    为了更友好方便的使用Lambda表达式 , JDK设计了函数式接口 . 函数式接口就是一个只有一个抽象方法的普通接口(除了唯一的抽象方法 , 可以有其他方法) , 它可以被隐式的转换为Lambda表达式 .

    java.lang.Runnable和java.util.concurrent.Callable是函数式接口的典型表现 . 在实际过程中 , 函数式接口是容易出错的 : 如果某个人在接口定义中增加了另一个方法 , 这时 , 这个接口就不再是函数式的了 , 并且编译过程也会失败 . 为了客服函数式接口的这种脆弱性并且显式说明某个接口是函数式接口 , Java8提供了一个特殊的注解@FunctionalInterface , 将它标注在接口定义上面 , 可以在编译阶段校验 . 如果接口定义了第二个抽象方法就会编译失败 . 该注解不是必须的(但强烈建议使用中加上该注解 , Java8库中的所有相关接口都已经带有这个注解了)

比较常见的接口类 : 
类名方法作用
Consumer<T>void accept(T t);处理对应的参数 , 无返回
Function<T , R>R apply(T t);处理参数t , 返回结果R
Predicate<T>boolean test(T t);根据参数t判断真假
Supplier<T>T get();拿到对应的结果
BiFunction<T , U , R>R apply(T t , U u);根据参数t , u , 得到结果R

    这些函数式接口在使用Lambda表达式时作为返回类型 , JDK定义了很多现在的函数式接口 , 实际自己也可以定义接口去作为表达式的返回 , 只是大多数情况下JDK定义的直接拿来用就可以了 . 而且这些接口在JDK8集合类使用操作流时被大量使用 . 

代码Demo : 
package com.xbz.java8.lambda;
​
/** 函数式接口 */
@FunctionalInterface
public interface FunctionalInterface1 {
​
    void test();//有且仅有一个抽象方法
​
    //默认方法和静态方法均不影响函数式接口
    default void defaultMethod(){
        System.out.println("FuncationalInterface1.defaultMethod");
    }
​
    static void staticMethod(){
        System.out.println("FuncationalInterface1.staticMethod");
    }
}
package com.xbz.java8.lambda;
​
public class FunctionalInterfaceDemo {
    public static void main(String[] args){
        FunctionalInterfaceDemo demo1 = new FunctionalInterfaceDemo();
​
        demo1.hello(new FunctionalInterface1() {//匿名内部类方式调用函数式接口
            @Override
            public void test() {
                System.out.println("Hello Lambda");
            }
        });
​
        demo1.hello(() -> System.out.println("Hello Lambda"));//Lambda方式调用函数式接口
    }
​
    /** 定义调用接口的方法 */
    public void hello(FunctionalInterface1 funcationalInterface1){
        funcationalInterface1.test();
    }
}
更详细的函数式接口文档 , 详见 http://www.runoob.com/java/java8-functional-interfaces.html
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值