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中,虽然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的含义及应用场景了。

posted @ 2020-10-15 10:40  轻风青枫  阅读(2620)  评论(0编辑  收藏  举报