关于lambda表达式访问外部变量有一个非常重要的限制的一点疑惑

weixin_37590827 2017-02-20 08:00:46
lambda表达式访问外部变量有一个非常重要的限制:变量不可变。
请问为什么:
String[] array = {"a", "b", "c"};
for(Integer i : Lists.newArrayList(1,2,3)){
Stream.of(array).map(item -> Strings.padEnd(item, i, '@')).forEach(System.out::println);
}可以执行呢?
而:
String[] array = {"a", "b", "c"};
for(int i = 1; i<4; i++){
Stream.of(array).map(item -> Strings.padEnd(item, i, '@')).forEach(System.out::println);
}就不可以?这两个i都是局部变量,并且不是final呀?

难道说在lambda语句执行过程中不可变就可以了吗?我觉得不太对,我查了一些for each的源代码,是不是因为第一个里的i指向的是迭代器里的next方法返回的对象这是不变的,而根据
  public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
next方法返回的对象是改变的。所以临时变量i是final的。
不知道理解的对不对,希望各位大神可以指点一下,谢谢

...全文
886 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
night field 2020-03-23
  • 打赏
  • 举报
回复 1
是因为,最终lambda表达式会被编译器编译成一个内部类。而内部类访问局部变量,必须要求该局部变量不可变(即楼上所说的effectively final,只有一次赋值操作)。为什么?因为Java不支持闭包,一个类(包括内部类)可以访问另一个类的成员变量,但是没法访问另一个类方法内的局部变量。那Java对内部类是如何支持访问局部变量的呢?答案是,编译器会将被引用到的局部变量,隐式地传递给该内部类,作为它的成员变量,正是因为这种做法,所以必须要求,局部变量应该要是不可变的。 具体可以参考下这个回答
流水晓风1 2020-03-23
  • 打赏
  • 举报
回复
for(int i=0;i<100;i++) { int k = i; k =k; pool.execute(() -> { for (int j = 0; j < 1000; j++) { System.out.println(k); mydata2.addPlusPlus(); } System.out.println(Thread.currentThread().getName()+" -- "+mydata2.number); }); } 从上面一段代码可以很清楚得看出,现在变量k是不能用的。但是,去掉k = k这一行,那么变量k在{}这一段里的作用域范围内就可以看作没变过,算作effectively final,能通过lambda表达式。
流水晓风1 2020-03-23
  • 打赏
  • 举报
回复
主要看lambda表达式引用的外部变量的作用域的范围。如果在其作用范围内变量没有修改过(变量没有出现过两次),哪怕没有final限制符,也是算作effectively final的。
流水晓风1 2020-03-23
  • 打赏
  • 举报
回复
上面的第一种增强for循环,可以改写成迭代语句,这样看得清楚一些:
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);
}
在while开始的语句块中,i只出现一次,符合efficient final的条件。
而普通for循环中的lambda表达式中引用的外部变量i,可以看出有明显的改变痕迹。因而是不允许的。

50,530

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧