Java 8 lambdas, Function.identity() или t-> t

У меня вопрос относительно использования метода Function.identity().

Представьте следующий код:

Arrays.asList("a", "b", "c")
          .stream()
          .map(Function.identity()) // <- This,
          .map(str -> str)          // <- is the same as this.
          .collect(Collectors.toMap(
                       Function.identity(), // <-- And this,
                       str -> str));        // <-- is the same as this.

Есть ли причина, по которой вам следует использовать Function.identity() вместо str->str (или наоборот). Я думаю, что второй вариант более читабельный (конечно, вопрос вкуса). Но есть ли "настоящая" причина, почему нужно быть предпочтительным?

Ответ 1

Как и в текущей реализации JRE, Function.identity() всегда будет возвращать один и тот же экземпляр, в то время как каждое вхождение identifier -> identifier не только создаст свой собственный экземпляр, но даже будет иметь отдельный класс реализации. Подробнее см. здесь.

Причина заключается в том, что компилятор генерирует синтетический метод, содержащий тривиальное тело этого лямбда-выражения (в случае x->x, эквивалентное return identifier;), и сообщает исполняющей среде создать реализацию функционального интерфейса, вызывающего это метод. Таким образом, среда выполнения видит только разные целевые методы, и текущая реализация не анализирует методы, чтобы выяснить, являются ли определенные методы эквивалентными.

Поэтому использование Function.identity() вместо x -> x может сэкономить некоторую память, но это не должно принимать ваше решение, если вы действительно считаете, что x -> x более читабельно, чем Function.identity().

Вы также можете подумать, что при компиляции с включенной информацией об отладке синтетический метод будет иметь атрибут отладки линии, указывающий на строку исходного кода, содержащую лямбда-выражение, поэтому у вас есть шанс найти источник определенного Function при отладке. В отличие от этого, когда вы сталкиваетесь с экземпляром, возвращаемым Function.identity() во время отладки операции, вы не знаете, кто вызвал этот метод и передал экземпляр операции.

Ответ 2

В вашем примере нет большой разницы между str -> str и Function.identity(), так как внутри это просто t->t.

Но иногда мы не можем использовать Function.identity. Взгляните сюда

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);

это скомпилирует штраф

int[] arrayOK = list.stream().mapToInt(i -> i).toArray();

но если вы попытаетесь скомпилировать

int[] arrayProblem = list.stream().mapToInt(Function.identity()).toArray();

вы получите ошибку компиляции, так как mapToInt ожидает ToIntFunction, которая не связана с Function. Также ToIntFunction не имеет метода identity().

Ответ 3

Из источника JDK:

static <T> Function<T, T> identity() {
    return t -> t;
}

Итак, нет, если это синтаксически корректно.