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的含義及應用場景了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM