如果曾经使用过C++的Lambda表达式,那么就会知道C++有一个变量捕获列表
有两种捕获方式,一种是引用捕获,一种是变量捕获。与C++函数的传引用、传值相对应。
而Java与C++不同 ,java只有引用捕获
例如:
ScheduledExecutorService ses = Executors.newScheduledThreadPool(5); int a=0; for(int i=1;i<=10;i++){ ses.schedule(()->{ System.out.println(a); },i,TimeUnit.MILLISECONDS); } |
通过编译 |
ScheduledExecutorService ses = Executors.newScheduledThreadPool(5); int a=0; for(int i=1;i<=10;i++){ ses.schedule(()->{ System.out.println(a); },i,TimeUnit.MILLISECONDS); } a=1; |
编译失败 |
那么此时我们可以输出i吗? 答案是不可以的
因为i并不final
那么如何才能输出变量i
i作为field是否可以呢?
private int i = 0; public void run(){ ScheduledExecutorService ses = Executors.newScheduledThreadPool(5); for(i=1;i<=10;i++){ ses.schedule(()->{ System.out.println(i); },i,TimeUnit.MILLISECONDS); } } |
编译通过,结果不正确 输出10个 “11” |
这是因为执行计划任务的时候 i已经变了。这也说明java不是值捕获。
此时可以创建final的变量作为跳板,帮助我们实现此功能
ScheduledExecutorService ses = Executors.newScheduledThreadPool(5); for(int i=1;i<=10;i++){ final int curi=i; ses.schedule(()->{ System.out.println(curi); },i,TimeUnit.MILLISECONDS); } |
为什么这样声明就可以了呢?
在创建lambda表达式的时候,lambda用到的外部引用都被复制了一份(因为局部变量在栈内存中,稍后会被销毁)。
这样看来java的lambda在需要捕获基础类型值的场景下,不如C++灵活
而final的限制,可以减少人为的错误