一.什么是Lambda

  • Lambda表达式也被称为箭头函数、匿名函数、闭包

  • 体现轻量级函数式编程思想

  • ‘->’左侧为操作参数,右侧是操作表达式

  • 基于JDK8+的新特性

  • 举例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public static void main(String[] args) {
    //1.传统模式下线程创建
    new Thread(new Runnable() {
    public void run() {
    System.out.println("threading..."+Thread.currentThread().getId());
    }
    }).start();

    //2.jdk8新特性,lambda表达式优化线程模式
    new Thread(()->{
    System.out.println("lambda threading..."+Thread.currentThread().getId());
    }).start();
    }
  • 注意:若提示java版本相关问题

    1
    2
    3
    4
    5
    6
    7
    8
    9
    报错举例:Information:java: javacTask: 源发行版 1.8 需要目标发行版 1.8

    原因:没有将jdk版本切换为1.8以上,下面三个地方的版本需要一致。

    1,Project Structure里确认两个地方:Project sdk以及project language level

    2,Project Structure->Modules里Sources里的Language level

    3,Preferences->java Compiler->Per-module bytecode Version

二.Lambda表达表达式基础知识

函数式接口

  • 只包含一个接口方法的特殊接口

  • 语义化检测注解:@FunctionalInterface

  • 举例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /**
    * 消息传输格式化转换接口
    */
    @FunctionalInterface
    public interface iMessageFormat {
    /**
    * 消息转换方法
    * @param message 要转换的消息
    * @param format 转换的格式
    * @return 返回转换后的数据
    */
    String format(String message, String format);
    }
  • 函数式接口中添加默认方法

    • 可以实现功能拓展,不影响原来的函数式接口

    • 接口类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      @FunctionalInterface
      public interface iUserCredential {
      /**
      * 通过用户账号,验证用户身份信息接口
      * @param username 要验证的用户账号
      * @return 返回身份信息【系统管理员 用户管理员 普通用户】
      */
      String vertifyUser(String username);

      //默认方法
      default String getCredential(String username){
      //模拟方法
      if("admin".equals(username)){
      return "admin+管理员用户";
      }else if("manager".equals(username)){
      return "manager+用户管理员用户";
      }else {
      return "commons+普通用户";
      }
      }

      }
    • 测试

      1
      2
      3
      4
      5
      public static void main(String[] args) {
      iUserCredential ic = new UserCredentialImpl();
      System.out.println(ic.vertifyUser("admin"));
      System.out.println(ic.getCredential("abc"));
      }
  • 函数式接口中添加静态方法

    • 接口类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      /**
      * 消息传输格式化转换接口
      */
      @FunctionalInterface
      public interface iMessageFormat {
      /**
      * 消息转换方法
      * @param message 要转换的消息
      * @param format 转换的格式
      * @return 返回转换后的数据
      */
      String format(String message, String format);

      /**
      * 消息合法性判断
      * @param msg 要验证的消息
      * @return 返回验证结果
      */
      //静态方法
      static boolean vertifyMessage(String msg){
      if (msg!=null){
      return true;
      }
      return false;
      }

      }
    • 测试

      1
      2
      3
      4
      5
      //此处直接调用接口静态方法
      if(iMessageFormat.vertifyMessage(msg)) {
      iMessageFormat format = new MessageFormatImpl();
      format.format("hello", "json");
      }
  • 总结

    1
    2
    3
    4
    5
    6
    函数式接口是只包含“一个方法”的接口,
    可用@FunctionalInterface注解进行检测,
    但“一个方法”不包括以下方法:
    1.默认接口方法(default
    2.静态接口方法(static
    3.继承自Object的方法( 例如toString() )

Lambda表达式与函数式接口关系

  • 传统匿名内部类实现接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //匿名内部类,实现接口抽象方法
    iUserCredential ic2 = new iUserCredential() {
    @Override
    public String vertifyUser(String username) {
    return "admin".equals(username)?"admin":"user";
    }
    };
    System.out.println(ic2.vertifyUser("manager"));
    System.out.println(ic2.vertifyUser("admin"));
  • 使用lambda表达式实现接口

    1
    2
    3
    4
    5
    6
    //lambda,针对函数式接口进行简单实现
    iUserCredential ic3 = (String username)->{
    return "admin".equals(username)?"admin":"user";
    };
    System.out.println(ic3.vertifyUser("admin"));
    System.out.println(ic3.vertifyUser("manager"));

    JDK中常见的函数式接口

  • java.util.function提供了大量函数式接口

    • java.util.function.Predicate<\T>

      1
      2
      3
      4
      5
      6
      //接收参数对象T,返回一个布尔类型
      Predicate<String> pre = (String username) -> {
      return "admin".equals(username);
      };
      System.out.println(pre.test("manager"));
      System.out.println(pre.test("admin"));
    • java.util.function.Comsumer<\T>

      1
      2
      3
      4
      5
      6
      7
      //此方法接收参数对象T,不反回结果
      //下面模拟消息发送
      Consumer<String> con = (String message) -> {
      System.out.println("要发送的消息:"+message);
      System.out.println("消息发送完成");
      };
      con.accept("I'm banana!");
    • java.util.function.Function<T,R>

      1
      2
      3
      4
      5
      6
      7
      //接收对象参数T,返回结果对象R
      //下面模拟性别判断
      Function<String, Integer> fun = (String gender) -> {
      return "male".equals(gender)?1:0;
      };
      System.out.println(fun.apply("male"));
      System.out.println(fun.apply("female"));
    • java.util.function.Supplier<\T>

      1
      2
      3
      4
      5
      //不接收参数,提供T对象的创建工厂
      Supplier<String> sup = () -> {
      return UUID.randomUUID().toString();
      };
      System.out.println(sup.get());
    • java.util.function.UnaryOperator<\T>

      1
      2
      3
      4
      5
      6
      7
      //接收参数对象T,返回结果对象T
      //下面模拟图片处理
      UnaryOperator<String> uo = (String img) -> {
      img += "[100*1020]";
      return img;
      };
      System.out.println(uo.apply("原图"));
    • java.util.function.BinaryOperator<\T>

      1
      2
      3
      4
      5
      6
      //接收两个T对象,返回一个T对象结果
      //下面模拟数字比大小
      BinaryOperator<Integer> bo = (Integer a, Integer b) -> {
      return a>b?a:b;
      };
      System.out.println(bo.apply(7,9));

      Lambda表达式基本语法

  • 基本语法

    1
    2
    3
    4
    5
    6
    7
    /*
    1)声明:和lambda表达式绑定的接口类型
    2)参数:包含在一对圆括号中,和绑定的接口中的抽象方法中的参数个数及顺序
    3)操作符:->
    4)执行代码块:包含在一对大括号中
    [接口声明] = (参数) -> {执行代码块};
    */
  • 注意

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    /*
    1)lambda表达式必须和接口进行绑定
    2)lambda表达式可以带任意个参数,参数类型可以不指定,jvm运行时会根据绑定的抽象方法进行判断
    3)lambda的返回值,代码块只有一行且没有大括号,可以省略return(即单行代码执行结果会自动返回)

    例如:Ilambda i1 = (x,y) -> x+y;
    接口:interface Ilambda{
    int test(int x, int y);
    }
    */

变量捕获

  • 匿名内部类中的变量捕获

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public void testInnerClass(){
    String s2 = "局部变量";

    new Thread(new Runnable() {
    String s3 = "内部变量";
    @Override
    public void run() {

    //访问全局变量
    //System.out.println(this.s1); //this关键字:当前内部类型对象,因此会报错
    System.out.println(s1);
    //局部变量访问:不能对局部变量数据进行修改[final]
    System.out.println(s2);
    //访问内部变量,可修改
    System.out.println(s3);
    System.out.println(this.s3);
    }
    }).start();
    }
  • Lambda表达式中的变量捕获

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public void testLambda(){
    String s2 = "局部变量lambda";

    new Thread( () -> {
    String s3 = "内部变量lambda";

    //访问全局变量
    System.out.println(this.s1); //this关键字表示方法所在类型的对象
    //访问局部变量,不能进行数据修改,默认为final
    System.out.println(s2);
    //访问内部变量,可修改
    System.out.println(s3);
    s3 = "lambda修改";
    System.out.println(s3);
    } ).start();
    }
  • 总结

    1
    2
    3
    lambda变量捕获优化了this关键字,使其不再单独建立内部对象作用域,即:
    1)lambda可以通过this访问全局变量
    2)普通匿名内部类通过this可访问的是内部变量

Lambda表达式类型检查

  • 案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class App {

    public static void test (MyInterface<String, List> inter){
    List<String> list = inter.strategy("hello", new ArrayList());
    System.out.println(list);
    }


    public static void main(String[] args) {

    test(new MyInterface<String, List>() {
    @Override
    public List strategy(String s, List list) {
    list.add(s);
    return list;
    }
    });

    test((x,y)->{
    y.add(x);
    return y;
    });

    }
    }

    @FunctionalInterface
    interface MyInterface<T, R>{
    R strategy(T t, R r);
    }
  • 表达式类型检查

    1
    (x,y)->{..} --> test(param) --> param==MyInterface --> lambda表达式为MyInterface类型
  • 参数类型检查

    1
    (x,y)->{..} --> MyInterface.strategy(T t, R r) --> 由MyInterface<String, List>确定具体类型 --> T==String R==list --> lambda --> (x,y) == strategy(T t, R r) --> x==T==String, y==R==list

    方法重载

  • 若定义了两个极其以上函数式接口,又进行了方法重载

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    public class App {

    interface Param1{
    void outInfo(String info);
    }

    interface Param2{
    void outInfo(String info);
    }

    //定义重载方法
    public void lambdaMethod(Param1 param){
    param.outInfo("hello param1 banana");
    }
    public void lambdaMethod(Param2 param){
    param.outInfo("hello param2 banana");
    }

    public static void main(String[] args) {
    App app = new App();

    //传统匿名内部类调用重载方法
    app.lambdaMethod(new Param1() {
    @Override
    public void outInfo(String info) {
    System.out.println(info);
    }
    });

    app.lambdaMethod(new Param2() {
    @Override
    public void outInfo(String info) {
    System.out.println(info);
    }
    });

    //lambda表达式方式,存在类型检查,lambda无法推导重载方法,要在参数列表中表明类型
    app.lambdaMethod((Param1) (String info)->{
    System.out.println(info);
    });
    }
    }

    Lambda底层原理简单分析

  • 编译时生成私有静态方法,在静态方法中做方法实现

  • 编译同时,针对lambda目标接口生成内部类型实现接口,完成对静态方法的调用


以上就是Lambda的一些基础知识,接下来可以学习Lambda进阶特性,即其在集合中的运用。