zoukankan      html  css  js  c++  java
  • Java 8 特性 —— lambda 表达式

    Lambda 表达式

    Lambda表达式本质上是一个匿名方法。常见的一个例子是,在 IDEA + JDK8 的环境下按照Java传统的语法规则编写一个线程:

    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello World!");
        }
    });

    IDEA会给出提示 Anonymous new Runnable() can be replaced with lambda less... (Ctrl+F1) ,说匿名内部类可以使用 Lambda 表达式替换。

    使用 Lambda 表达式则只需要使用一句话就可代替上面使用匿名类的方式。

    new Thread(() -> System.out.println("Hello World!"));

    在这个例子中,传统的语法规则,我们是将一个匿名内部类作为参数进行传递,我们实现了Runnable接口,并将其作为参数传递给Thread类,这实际上我们传递的是一段代码,也即我们将代码作为了数据进行传递,这就带来许多不必要的“样板代码”。

    在JDK8之前,Java是不支持函数式编程的,所谓的函数编程,即可理解是将一个函数(也称为“行为”)作为一个参数进行传递。通常我们提及得更多的是面向对象编程,面向对象编程是对数据的抽象(各种各样的POJO类),而函数式编程则是对行为的抽象(将行为作为一个参数进行传递)。在JavaScript中这是很常见的一个语法特性,但在Java中将一个函数作为参数传递这却行不通,好在JDK8的出现打破了Java的这一限制。

    如上例所见,Lambda表达式一共有三部分组成:参数列表,箭头(->),以及一个表达式或语句块。

    Lambda 表达式和函数式接口结合

    什么场景下可以使用 Lambda 表达式?

    能够接收 Lambda 表达式的参数类型,是一个只包含一个抽象方法的接口。只包含一个抽象方法的接口为“函数式接口”。

    例如上例,Runnable 接口只包含一个抽象方法,所以它被称为“函数式接口”,所以它可以使用 Lambda 表达式来代替匿名内部类,作为 Thread 的构造参数。

    函数式接口有无入参和有无返回值,可分为四种情形:

    无参无返回值
    有参无返回值
    无参有返回值
    有参有返回值

    Lambda 表达式和这四种形式的函数式接口结合也就有四种情况;

    无参无返回值

    package com.boomoom;
    
    @FunctionalInterface
    public interface InterfaceWithNoParam {
        void run();
    }
    package com.boomoom;
    
    import org.junit.Test;
    
    public class FunctionInterfaceTest {
        //匿名内部类
        InterfaceWithNoParam param1 = new InterfaceWithNoParam() {
            @Override
            public void run() {
                System.out.println("通过匿名内部类实现run()");
            }
        };
        //Lambda表达式
        //空括号表示无参
        InterfaceWithNoParam param = () -> System.out.println("通过Lambda表达式实现run()") ;
    
        @Test
        public void testInterfaceWithNoParam() {
            this.param.run();
            this.param1.run();
        }
    }
    通过Lambda表达式实现run()
    通过匿名内部类实现run()

    有参无返回值

    package com.boomoom;
    
    @FunctionalInterface
    public interface InterfaceWithParam {
        void run(String s);
    }
    package com.boomoom;
    
    import org.junit.Test;
    
    public class FunctionInterfaceTest {
    
        InterfaceWithParam param = new InterfaceWithParam() {
            @Override
            public void run(String s) {
                System.out.println("通过" + s + "实现run(String)");
            }
        };
        InterfaceWithParam param1 = (String s) -> System.out.println("通过" + s + "实现run(String)");
    
        @Test
        public void testInterfaceWithParam() {
            this.param.run("匿名类");
            this.param1.run("Lambda");
        }
    }
    通过匿名类实现run(String)
    通过Lambda实现run(String)

    Lambda 表达式“(String) -> Sysout.out.println("通过" + s + "实现run(String)")”,左边传递的是参数,此处指明参数类型,也可不指定,通过上下文进行类型推导;但在有些情况下不能推导出参数类型(在编译时不能推导通常IDE会提示),此时则需要指明参数类型。我个人建议,任何情况下指明函数的参数类型

    哪种情况不能推导出参数类型呢?就是函数接口是一个泛型的时候。

    @FunctionalInterface
    public interface InterfaceWithParam<T> {
        void run(T param);
    }

    无参有返回值

    package com.boomoom;
    
    @FunctionalInterface
    public interface InterfaceUnVoidWithNoParam {
        String run();
    }
    package com.boomoom;
    
    import org.junit.Test;
    
    public class FunctionInterfaceTest {
    
        InterfaceUnVoidWithNoParam interfaceUnVoidWithNoParam = new InterfaceUnVoidWithNoParam() {
            @Override
            public String run() {
                return "Hello 匿名内部类!";
            }
        };
        InterfaceUnVoidWithNoParam interfaceUnVoidWithNoParam1 = () -> "Hello Lambda!";
    
        @Test
        public void test() {
            System.out.println("返回结果是:"+ this.interfaceUnVoidWithNoParam.run());
            System.out.println("返回结果是:"+ this.interfaceUnVoidWithNoParam1.run());
        }
    }
    返回结果是:Hello 匿名内部类!
    返回结果是:Hello Lambda!

    有参有返回值

    package com.boomoom;
    
    @FunctionalInterface
    public interface InterfaceUnVoidWithParam {
        String run(Integer i);
    }
    package com.boomoom;
    
    import org.junit.Test;
    
    public class FunctionInterfaceTest {
    
        InterfaceUnVoidWithParam interfaceWithParams = new InterfaceUnVoidWithParam() {
            @Override
            public String run(Integer integer) {
                return String.valueOf(integer);
            }
        };
        InterfaceUnVoidWithParam interfaceWithParams1 = (Integer integer) -> String.valueOf(integer);
    
        @Test
        public void test() {
            System.out.println("您输入的是:"+ this.interfaceWithParams.run(1));
            System.out.println("您输入的是:"+ this.interfaceWithParams1.run(2));
        }
    }
    您输入的是:1
    您输入的是:2

    这四种基本情况已经大致清楚了,特别是需要弄清,什么时候可以使用 Lambda 表达式代替匿名内部类,也就是 Lambda 表达式的应用场景是函数式接口。Lambda 表达式这一新特性在 JDK8 中的引入,更大的好处则是集合 API 的更新,新增的 Stream 类库,使得我们在遍历使用集合时不再像以往那样不断地使用 for 循环。

    JDK8 使用集合

    示例:计算来自“hangzhou”的学生数量有多少。

    在JDK8 前的代码:

    for (Student student : studentList) {
        if (student.getCity().equals("hangzhou")) {
            count++;
        }
    }

    JDK8 使用集合的正确姿势:

    count = studentList.stream().filter((student -> student.getCity().equals("hangzhou"))).count();

    API 的使用“难度”恰似提高了,实际只是不熟悉而已。传统迭代的方式需要阅读完整个循环才能明白代码逻辑,JDK8 通过流的方式则可以望文生义且代码量大大减小。

    其中最为重要的是 Stream 流。Stream 的是通过函数式编程方式实现的在集合类上进行复杂操作的工具。关于 Stream 的用法和实现方式,详见此篇。使用JDK8的开发环境进行开发,尽量使用新的集合操作 API。

    参考:https://www.cnblogs.com/yulinfeng/p/8452379.html

    http://www.runoob.com/java/java8-lambda-expressions.html

    https://www.cnblogs.com/yw0219/p/7302597.html

    https://blog.csdn.net/io_field/article/details/54380200

  • 相关阅读:
    iPad 3g版完美实现打电话功能(phoneitipad破解)
    vb.NET基础总结
    PMP考试的过与只是
    Oracle基础学习5-- Oracle权限之”角色”
    linux内存操作----kernel 3.5.X copy_from_user()和copy_to_user()
    猜数字游戏
    pthread_t definition
    POJ 2057 The Lost House
    简单截图功能实现
    java实现罗马数字转十进制
  • 原文地址:https://www.cnblogs.com/boomoom/p/10449442.html
Copyright © 2011-2022 走看看