• java8新特性,Lambda 表达式


    1.Lambda 表达式简介

    Lambda 表达式,也称为闭包,是 Java 8 中最大和最令人期待的语言改变。它允许我们将函数当成参数传递给某个方法,或者把代码本身当作数据处理,函数式开发者非常熟悉这些概念。

    很多JVM平台上的语言(Groovy、Scala等)从诞生之日就支持 Lambda 表达式,但是 Java 开发者没有选择,只能使用匿名内部类代替Lambda表达式。

    使用 Lambda 表达式可以使代码变的更加简洁紧凑。让 Java 也能支持简单的函数式编程🐖

    表达式语法:

    (parameters) -> expression 
    或 
    (parameters) ->{ statements; } 
    
    • 1
    • 2
    • 3

    Lambda 编程风格,可以总结为四类:

    • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
    • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号
    • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号
    • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值

    2.Lambda 实战

    替代匿名内部类

    过去给方法传动态参数的唯一方法是使用内部类。比如我们想实现一个集合的排序比较方法:

    ArrayList<Integer> arrayList = new ArrayList<>();
    arrayList.add(521);
    arrayList.add(1314);
    // 使用内部类的方式实现排序
    arrayList.sort(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    });
    System.out.println(arrayList);  // [1314, 521]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    使用Lambda表达式简化内部类的操作:

    ArrayList<Integer> arrayList = new ArrayList<>();
    arrayList.add(521);
    arrayList.add(1314);
    // Lambda
    arrayList.sort((Integer o1, Integer o2) -> o2 - o1);
    System.out.println(arrayList);  // [1314, 521]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    再比如,在Java8之前,我们使用内部类的方式开启一个新的线程:

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我是传统的多线程操作!");
            }
        }).start();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    使用Lambda表达式,一切都变得简洁起来:

    new Thread(() -> System.out.println("我是Lambda表达式开启的线程")).start();
    
    • 1

    那它对接口有没有要求呢?我们发现这些匿名内部类只重写了接口的一个方法,当然也只有一个方法须要重写。这就是我们上文提到的函数式接口,也就是说只要方法的参数是函数式接口都可以用 Lambda 表达式。

    @FunctionalInterface
    public interface Comparator<T>{}
    
    @FunctionalInterface
    public interface Runnable{}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    集合迭代

    传统foreach方法遍历集合:

    List<String> strings = Arrays.asList("1", "2", "3");
    for (String string : strings) {
        System.out.println(string);
    }
    
    • 1
    • 2
    • 3
    • 4

    使用Lambda表达式遍历:

    List<String> strings = Arrays.asList("1", "2", "3");
    strings.forEach((s) -> System.out.println(s));
    // map的遍历方法
    map.forEach((k,v)->System.out.println(v));
    
    • 1
    • 2
    • 3
    • 4

    3.Lambda表达式的五种使用场景

    第一种场景:无参,无返回值类型

    /**
     * Lambda表达式快捷开启线程
     * 第一种:无参,无返回值类型
     */
    @Test
    public void test1() {
        Runnable runnable = () -> System.out.println("我爱中国!");
        runnable.run();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    第二种场景:需要参数,但是没有返回值

    /**
      * 需要参数,但是没有返回值
      */
     @Test
     public void test2() {
         Consumer<String> con = (String s) -> {
             System.out.println("s=" + s);
         };
         con.accept("dahe");
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    场景三:类型推断

    /**
     * 类型推断
     * 数据类型可以进行省略操作,可以由编译器进行类型推断
     */
    @Test
    public void test3() {
        Consumer<String> con = (s) -> {
            System.out.println("s=" + s);
        };
        con.accept("dahe");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    场景四:参数列表只有一个参数,可以省略括号

    @Test
    public void test4() {
        Consumer<String> con = s -> {
            System.out.println("s=" + s);
        };
        con.accept("dahe");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    场景五:参数列表有多个参数,并且可以有返回值

    /**
     * 参数列表有多个参数,并且可以有返回值
     */
    @Test
    public void test5() {
        Comparator<Integer> comparator = (o1, o2) -> {
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };
        System.out.println(comparator.compare(521, 1314));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.函数式接口

    定义:也称 SAM 接口,函数接口指的是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,这样的接口可以隐式转换为 Lambda 表达式。

    但是在实践中,函数式接口非常脆弱,只要某个开发者在该接口中添加一个函数,则该接口就不再是函数式接口进而导致编译失败。为了克服这种代码层面的脆弱性,并显式说明某个接口是函数式接口,Java 8 提供了一个特殊的注解@FunctionalInterface

    一些没有@FunctionalInterface 注解,但是只要符合函数式接口的定义就是函数式接口,与是否有@FunctionalInterface注解无关,注解只是在编译时起到强制规范定义的作用。其在 Lambda 表达式中有广泛的应用。

    Lambda表达式的本质:作为函数式接口的实例🐏

    以下是一个函数式接口的案例:

    @FunctionalInterface 
    public interface GreetingService { 
        void sayMessage(String message); 
    }
    
    • 1
    • 2
    • 3
    • 4

    5.方法引用

    Java 8 允许使用 :: 关键字来传递方法或者构造函数引用,无论如何,表达式返回的类型必须是 functional-interface。

    例子:

    之前我们使用Lambda表达式遍历一个集合:

    List<String> strings = Arrays.asList("1", "2", "3");
    strings.forEach((s) -> System.out.println(s));
    
    • 1
    • 2

    使用方法引用,它还可以更加的简洁:

    List<String> strings = Arrays.asList("1", "2", "3");
    strings.forEach(System.out::println);
    
    • 1
    • 2

    之前使用Lambda表达式实现的Consumer:

    Consumer<String> con = s -> {
        System.out.println("s=" + s);
    };
    con.accept("dahe");
    
    • 1
    • 2
    • 3
    • 4

    使用方法引用还可以更加的简洁:

    Consumer<String> con = System.out::println;
    con.accept("dahe");
    
    • 1
    • 2

    综合实例:

    public class LambdaClassSuper {
        LambdaInterface sf(){
            return null;
        }
    }
    
    public class LambdaClass extends LambdaClassSuper {
        public static LambdaInterface staticF() {
            return null;
        }
    
        public LambdaInterface f() {
            return null;
        }
    
        void show() {
            // 1.调用静态函数,返回类型必须是functional-interface
            LambdaInterface t = LambdaClass::staticF;
    
            // 2.实例方法调用
            LambdaClass lambdaClass = new LambdaClass();
            LambdaInterface lambdaInterface = lambdaClass::f;
    
            // 3.超类上的方法调用
            LambdaInterface superf = super::sf;
    
            // 4. 构造方法调用
            LambdaInterface tt = LambdaClassSuper::new;
        }
    }
    
    • 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
  • 相关阅读:
    clang分析iOS的block实现
    sql添加索引
    业界新标杆!阿里开源自研高并发编程核心笔记(2022 最新版)
    小程序canvas层级过高真机遮挡组件的解决办法
    解决Java中https请求接口报错问题
    Windows下启动freeRDP并自适应远端桌面大小
    学校图书馆管理系统
    广告原生化发展,助力开发者收益更上一层楼
    南卡电容笔和益博思哪个更好用?平板电脑值得入手电容笔对比
    Codeforces Round #821 (Div. 2)A~D1
  • 原文地址:https://blog.csdn.net/Gherbirthday0916/article/details/126520350