zoukankan      html  css  js  c++  java
  • Lambda表达式中引用变量的问题

    Lambda表达式中引用变量的问题

     

    Lambda表达式内部自定义的变量肯定没问题。引用的外部final变量也没问题。问题在于effectively final变量的理解,及应用场景的认识。引用的外部变量没有加final限定符,只要没有发生过改变,就可以当作是事实上的final变量。变量没改变过,就是说Lambda表达式引用的外部变量在它所在的作用域范围内,只赋值过一次**,该变量名称只出现过一次**,就算是effectively final。举例说明。
    例1:增强for循环,实质上使用的是迭代器(参见例3)。

    String[] array = {"a", "b", "c"};
            for(Integer i : Arrays.asList(1,2,3)){
                Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
                        .forEach(System.out::println);
            }

    在例1中,虽然for循环代码块中变量 i 的值是变化的,但是 i 只出现过一次,没有明显的改变,也算事实上的final,Lambda表达式能通过。与之相反的示例,看例2。
    例2:普通for循环。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
                        .forEach(System.out::println);
            }

    在例2中,Lambda表达式引用的外部变量 i 所在的作用域范围是for循环体。明显有两次赋值 i =1 , i++。要报错误提示:Variable used in lambda expression should be final or effectively final。变量 i 不能算是effectively final。

    例3:迭代器。

    String[] array = {"a", "b", "c"};
            List<Integer> list = Arrays.asList(1, 2, 3);
            Iterator<Integer> iterator = list.iterator();
            while(iterator.hasNext() ){
                int i = iterator.next();
                Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
                        .forEach(System.out::println);
            }

    增加for循环也会转为迭代器调用模式。可以明显看出变量 i 只出现过一次有效赋值,没有修改过,算是effectively final。Lambda表达式能通过。

    例4: 将普通for循环变量赋给一个中间变量,可通过。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                int k = i;
                Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
                        .forEach(System.out::println);
            }

    因为变量 k 的作用域是for循环的一次{},变量 k 在一次{}代码块中只赋值过一次后,没有再被改变过,即effectively final。Lambda表达式能通过。再举两个很近似的例子,就更能说明问题。
    例4-2: 变量的判断不算是被修改。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                int k = i;
                if(k>2){
                    System.out.println(k);
                }
                Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
                        .forEach(System.out::println);
            }

    该Lambda表达式能通过。因为 k 只有过一次赋值修改后就没有改变过。k 的后两次出现只是读取使用,不是写入。

    例4-3: 自己赋给自己也算是再次被修改。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                int k = i;
                k = k;
                Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
                        .forEach(System.out::println);
            }

    这个Lambda表达式也会报错。因为变量 k 出现了两次赋值,不符合final的定义。这样多举几个例子测试,就算彻底搞明白了effectively final的含义及应用场景了。

    Lambda表达式中引用变量的问题

     

    Lambda表达式内部自定义的变量肯定没问题。引用的外部final变量也没问题。问题在于effectively final变量的理解,及应用场景的认识。引用的外部变量没有加final限定符,只要没有发生过改变,就可以当作是事实上的final变量。变量没改变过,就是说Lambda表达式引用的外部变量在它所在的作用域范围内,只赋值过一次**,该变量名称只出现过一次**,就算是effectively final。举例说明。
    例1:增强for循环,实质上使用的是迭代器(参见例3)。

    String[] array = {"a", "b", "c"};
            for(Integer i : Arrays.asList(1,2,3)){
                Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
                        .forEach(System.out::println);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在例1中,虽然for循环代码块中变量 i 的值是变化的,但是 i 只出现过一次,没有明显的改变,也算事实上的final,Lambda表达式能通过。与之相反的示例,看例2。
    例2:普通for循环。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
                        .forEach(System.out::println);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在例2中,Lambda表达式引用的外部变量 i 所在的作用域范围是for循环体。明显有两次赋值 i =1 , i++。要报错误提示:Variable used in lambda expression should be final or effectively final。变量 i 不能算是effectively final。

    例3:迭代器。

    String[] array = {"a", "b", "c"};
            List<Integer> list = Arrays.asList(1, 2, 3);
            Iterator<Integer> iterator = list.iterator();
            while(iterator.hasNext() ){
                int i = iterator.next();
                Stream.of(array).map(item -> Strings.padEnd(item, i, '@'))
                        .forEach(System.out::println);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    增加for循环也会转为迭代器调用模式。可以明显看出变量 i 只出现过一次有效赋值,没有修改过,算是effectively final。Lambda表达式能通过。

    例4: 将普通for循环变量赋给一个中间变量,可通过。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                int k = i;
                Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
                        .forEach(System.out::println);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    因为变量 k 的作用域是for循环的一次{},变量 k 在一次{}代码块中只赋值过一次后,没有再被改变过,即effectively final。Lambda表达式能通过。再举两个很近似的例子,就更能说明问题。
    例4-2: 变量的判断不算是被修改。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                int k = i;
                if(k>2){
                    System.out.println(k);
                }
                Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
                        .forEach(System.out::println);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    该Lambda表达式能通过。因为 k 只有过一次赋值修改后就没有改变过。k 的后两次出现只是读取使用,不是写入。

    例4-3: 自己赋给自己也算是再次被修改。

     String[] array = {"a", "b", "c"};
            for(Integer i = 1; i<4; i++){
                int k = i;
                k = k;
                Stream.of(array).map(item -> Strings.padEnd(item, k, '@'))
                        .forEach(System.out::println);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这个Lambda表达式也会报错。因为变量 k 出现了两次赋值,不符合final的定义。这样多举几个例子测试,就算彻底搞明白了effectively final的含义及应用场景了。

  • 相关阅读:
    使用iframe从网页调起移动端应用
    综合练习:词频统计
    组合数据类型练习
    python 绘制五星红旗
    熟悉常用的Linux操作
    大数据概述
    windowBackground打造闪屏页
    安卓日期选择对话框
    c语言语法分析器
    C语言文法
  • 原文地址:https://www.cnblogs.com/skyblue123/p/13819163.html
Copyright © 2011-2022 走看看