你是否了解 Java 中的 Lambda 表达式?以及它的变量捕获机制?

3552 篇文章 115 订阅

1. Lambda 表达式

1.1 语法

  Lambda表达式是Java SE 8引入的一种函数式编程语法,它的语法形式是:

rust复制代码(parameters) -> expression

(parameters) -> { statements; }
  • parameters是参数列表,可以是空的或者包含一个或多个参数。如果有多个参数,用逗号分隔。
  • ->是Lambda运算符,将参数列表和Lambda表达式的主体分开。
  • expression是一个表达式,可以是常量、变量、方法调用、操作符、Lambda表达式或其他表达式。
  • { }中的语句是Lambda表达式的主体,可以是一个或多个语句,也可以是一个代码块,是函数式接口里方法的实现。
Java复制代码// 1. 不需要参数,返回值为 2
() -> 2
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的和
(x, y) -> x + y
// 4. 接收2个int型整数,返回他们的乘积
(int x, int y) -> x * y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

1.2 函数式接口

  Lambda表达式必须搭配函数式接口使用。

  函数式接口定义:一个接口有且只有一个抽象方法 。

形如下面的接口:

java复制代码interface FunctionalInterface{

    void print();
    
    //可以加 default 方法,只要保证接口中只有一个抽象方法就行了
    default void test2() {
        System.out.println("JDK1.8新特性,default默认方法可以有具体的实现");
    }
}

1.3 Lambda 的使用

  现有如下的函数接口:

java复制代码//无返回值无参数
interface NoParameterNoReturn {
    void test();
}

//无返回值一个参数
interface OneParameterNoReturn {
    void test(int a);
}

//无返回值多个参数
interface MoreParameterNoReturn {
    void test(int a,int b);
}

//有返回值无参数
interface NoParameterReturn {
    int test();
}

//有返回值一个参数
interface OneParameterReturn {
    int test(int a);
} 

//有返回值多参数
interface MoreParameterReturn {
    int test(int a,int b);
}
java复制代码public static void main(String[] args) {

    //无返回值无参数
    NoParameterNoReturn noParameterNoReturn = ()-> System.out.println("通过Lambda的方式:这是 test 方法");
    NoParameterNoReturn noParameterNoReturn1 = new NoParameterNoReturn() {
        @Override
        public void test() {
            System.out.println("通过匿名内部类的方式:这是 test 方法");
        }
    };
    noParameterNoReturn.test();
    noParameterNoReturn1.test();
}
结果:
通过Lambda的方式:这是 test 方法
通过匿名内部类的方式:这是 test 方法

   Lambda 与匿名内部类非常相似,它们的作用都是创建一个匿名类并重写里面的方法,但是 Lambda 的形式比较简单,但是只适用于函数接口;而匿名内部类可用于接口中多个抽象方法的情况。

java复制代码public class Main {
    public static void main(String[] args) {

        //无返回值无参数
        NoParameterNoReturn noParameterNoReturn = ()-> System.out.println("通过Lambda的方式:这是 test 方法");
        noParameterNoReturn.test();

        //无返回值一个参数,可以省略:a->System.out.println(a);
        OneParameterNoReturn oneParameterNoReturn = (int a)-> System.out.println(a);
        oneParameterNoReturn.test(100);

        //无返回值多个参数,int 可以省略:(a,b)-> System.out.println("a: " + a+" " + "b: " + b);
        MoreParameterNoReturn moreParameterNoReturn = (int a,int b)-> System.out.println("a: " + a+" " + "b: " + b);
        moreParameterNoReturn.test(200,300);

        //有返回值无参数,return 可以省略: ()->100;
        NoParameterReturn noParameterReturn = ()-> {
            int a = 100;
            return a;
        };
        System.out.println(noParameterReturn.test());

        //有返回值多参数
        MoreParameterReturn moreParameterReturn = (int a,int b)->{
            return a + b;
        };
        System.out.println(moreParameterReturn.test(100, 200));
    }
}
  1. 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
  2. 参数的小括号里面只有一个参数,那么小括号可以省略。
  3. 如果方法体当中只有一句代码,那么大括号可以省略。
  4. 如果方法体中只有一条语句,其是return语句,那么大括号可以省略,且去掉return关键字。

2. 变量捕获

2.1 匿名内部类的变量捕获

  所谓变量捕获就是:匿名内部类中访问所在方法或代码块中的局部变量,这个过程被称为“变量捕获”。

typescript复制代码public static void main(String[] args) {
    //变量捕获
    int a = 100;
    NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
        @Override
        public void test() {
            //捕获 a 变量
            System.out.println(a);   
        }
    };
    noParameterNoReturn.test();
}
结果:
100

  但是,只有在局部变量是“有效的最终变量”时,匿名内部类才能够引用它。如果局部变量不是有效的最终变量,则无法在匿名内部类中引用它,会导致编译错误。

  有效的最终变量指的是一个在生命周期中没有被修改过的局部变量,它可以被认为是一个常量。

2.2 Lambda 的变量捕获

  同样的,Lambda 的变量捕获也是只有在局部变量是“有效的最终变量”时,才能够引用它。

java复制代码public static void main(String[] args) {
    //变量捕获
    int a = 100;
    
    NoParameterNoReturn noParameterNoReturn = ()->{
        //捕获 a 变量
        System.out.println(a);
    };
    noParameterNoReturn.test();
}
结果:
100

2.3 匿名内部类的变量捕获 与 Lambda 的变量捕获的区别

  区别:Lambda表达式可以捕获外面的this,而匿名内部类无法直接捕获外面的this。

  在Lambda表达式中,this关键字引用的是Lambda表达式所在的类的实例,而不是Lambda表达式内部的类的实例。这个特性被称为“闭包”。

  匿名内部类中的this关键字引用的是匿名内部类的实例,而不是外部类的实例。

java复制代码public class Student {
    //外部类的属性
    String name = "Student";
    int age;

     NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
        //内部类自己的属性
        String name = "hello";
        @Override
        public void test() {
            System.out.println(this.name);
        }
    };

     NoParameterNoReturn noParameterNoReturn2 = ()->{
        String name = "hello";
        System.out.println(this.name);
    };

    public static void main(String[] args) {
        Student student = new Student();
        student.noParameterNoReturn.test();
        student.noParameterNoReturn2.test();
    }
}

结果:

  如果想让匿名内部类的捕获外面的 this,就需要使用外部类的类名或对象引用来访问。。

java复制代码public class Student {
    //外部类的属性
    String name = "Student";
    int age;

     NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
        //内部类自己的属性
        String name = "hello";
        @Override
        public void test() {
            System.out.println(Student.this.name);
        }
    };
}
结果:
Student

3. Lambda 在集合当中的使用

对应的接 口

新增的方法

Collection

removeIf() spliterator() stream() parallelStream() forEach()

List

replaceAll() sort()

Map

getOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge()

3.1 Collection 接口中的 forEach() 方法

  这个方法是在接口 Iterable 当中的:

  这个方法表示用于对集合中的每个元素执行指定的操作。

java复制代码public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");

        //匿名内部类 遍历 list
        list.forEach(new Consumer<String>() {
            //执行的操作
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });

        System.out.println("---------------------");
        
        //Lambda 遍历 list
        list.forEach((String s)->{
            System.out.println(s);
        });
    }
}
结果:
Hello
bit
hello
lambda
---------------------
Hello
bit
hello
lambda

3.2 List 接口中的 sort() 方法

java复制代码public class Test {
    public static void main(String[] args) {

        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");
        //匿名内部类
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println(list);


        List<String> list2 = new ArrayList<>();
        list2.add("hello");
        list2.add("lambda");        
        list2.add("Hello");
        list2.add("bit");
        //Lambda
        list2.sort((o1,o2)->o1.compareTo(o2));
        System.out.println(list2);
    }
}

3.3 Map接口的 forEach() 方法

  这个方法的源码:

java复制代码public static void main(String[] args) {
    Map<Integer, String> map = new HashMap<>();
    map.put(1, "hello");
    map.put(2, "bit");
    map.put(3, "hello");
    map.put(4, "lambda");
    //匿名内部类
    map.forEach(new BiConsumer<Integer, String>() {
        @Override
        public void accept(Integer integer, String s) {
            System.out.println(integer + "=" + s);
        }
    });
    System.out.println();
    
    //Lambda
    map.forEach((integer,s)->{System.out.println(integer + "=" + s);});
}
结果:
1=hello
2=bit
3=hello
4=lambda

1=hello
2=bit
3=hello
4=lambda

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值