Подтвердить что ты не робот

Java Lambda Ссылка на объект Enclosing: Заменить частным статическим классом?

Java lambda, ссылающаяся на элемент из своей охватывающей области, содержит ссылку на свой охватывающий объект. Надуманный пример: lambda hold to myClass:

class MyClass {
  final String foo = "foo";
  public Consumer<String> getFn() {
    return bar -> System.out.println(bar + foo);
  }
}

Это проблематично, если время жизни лямбда длинное; то у нас есть ссылка на MyClass, которая долговечна, когда она иначе выйдет за рамки. Здесь мы можем оптимизировать, заменив лямбда частным статическим классом, так что мы используем только ссылку на нужную нам строку, а не на весь класс:

class MyClass {

  private static class PrintConsumer implements Consumer<String> {

    String foo;

    PrintConsumer(String foo) {
      this.foo = foo;
    }

    @Override
    public void accept(String bar) {
      System.out.println(bar + foo);
    }
  }

  final String foo = "foo";

  public Consumer<String> getFn() {
    return new PrintConsumer(foo);
  }
}

К сожалению, это супервероятно и уничтожает хороший синтаксис, который мы получаем от использования (эффективно конечных) переменных из охватывающей области в lambdas. Это технически оптимально? Всегда существует компромисс между хорошим синтаксисом и возможностью сохранения ref дольше, чем необходимо?

4b9b3361

Ответ 1

Сначала назначьте свой член локальной переменной:

class MyClass {
  final String foo = "foo";
  private Consumer<String> getFn() {
    String localFoo = foo;
    return bar -> System.out.println(bar + localFoo);
  }
}

Теперь лямбда фиксирует только локальные переменные внутри getFn(). MyClass.this больше не фиксируется.

Другой вариант, немного более подробный, делегировать вспомогательный метод:

class MyClass {
  final String foo = "foo";
  private Consumer<String> getFn() {
    return getFn(foo);
  }
  private static Consumer<String> getFn(String localFoo) {
    return bar -> System.out.println(bar + localFoo);
  }
}

Ответ 2

Комбинация решений Lukas Eder для локальной конечной переменной и вспомогательных методов:

class MyClass {
  final String foo = "foo";
  private Consumer<String> getFn() {
    return apply(
      foo,
      localFoo -> bar -> System.out.println(bar + localFoo)
    );
  }
  private static <IN, OUT> OUT apply(
    final IN in,
    final Function<IN, OUT> function
  ) {
    return function.apply(in);
  }
}