В java8, как установить глобальное значение в блоке lambdas foreach?

    public void test(){
       String x;
       List<String> list=Arrays.asList("a","b","c","d");

       list.forEach(n->{
          if(n.equals("d"))
            x="match the value";
       });
    }

1. Как и код выше, я хочу установить значение переменной рядом с блоком foreach, может ли он работать?

2. И почему?

3. Итератор foreach в порядке или беспорядке?

4. Я думаю, что блок lamdas foreach классный и простой для итератора, но это действительно сложная задача, а не одна и та же работа в java 7 или раньше.

Ответы

Ответ 1

Вы могли бы, конечно, "сделать внешнее значение изменчивым" с помощью трюка:

public void test() {
    String[] x = new String[1];
    List<String> list = Arrays.asList("a", "b", "c", "d");

    list.forEach(n -> {
        if (n.equals("d"))
            x[0] = "match the value";
    });
}

Приготовьтесь к избиению функциональным пуристом в команде. Однако гораздо приятнее использовать более функциональный подход (похожий на подход Sleiman):

public void test() {
    List<String> list = Arrays.asList("a", "b", "c", "d");
    String x = list.stream()
                   .filter("d"::equals)
                   .findAny()
                   .map(v -> "match the value")
                   .orElse(null);
}

Ответ 2

  • Нет, вы не можете этого сделать. (Хотя вы должны были попробовать это самостоятельно)
  • Поскольку переменные, используемые внутри анонимных внутренних классов и лямбда-выражения, должны быть effectively final.
  • вы можете добиться более точного использования с помощью filter и map.

    Optional<String> d = list.stream()
                             .filter(c -> c.equals("d"))
                             .findFirst()
                             .map(c -> "match the value");
    

Ответ 3

В дополнение к уже предоставленным идиоматическим примерам, другой взлом должен был бы использовать AtomicReference, но я бы рекомендовал его, только если вам нужно "forEach" и предпочитайте что-то более читаемое, чем функционально-функциональный вариант:

public void test(){
    AtomicReference<String> x = new AtomicReference<>();
    List<String> list= Arrays.asList("a", "b", "c", "d");

    list.forEach(n->{
        if(n.equals("d"))
            x.set("match the value");
    });
}

Ответ 4

Как уже объяснялось, вы не можете изменить локальную переменную внешнего метода из тела лямбда (а также из тела анонимного класса). Мой совет - не пытайтесь использовать лямбды, когда они совершенно не нужны. Ваша проблема может быть решена следующим образом:

public void test(){
   String x;
   List<String> list = Arrays.asList("a","b","c","d");
   if(list.contains("d"))
       x = "match the value";
}

В общем, lambdas дружат с функциональным программированием, где у вас редко есть изменяемые переменные (каждая переменная назначается только один раз). Если вы используете лямбды, но продолжаете думать в императивном стиле, у вас всегда будут такие проблемы.