Ссылка на метод Java8, используемая как объект Function для объединения функций
Есть ли способ в Java8 использовать ссылку метода как объект Function
для использования своих методов, например:
Stream.of("ciao", "hola", "hello")
.map(String::length.andThen(n -> n * 2))
Этот вопрос не связан с Stream
, он используется как пример, я хотел бы получить ответ о ссылке метода
Ответы
Ответ 1
Вы можете написать статический метод для этого:
import java.util.function.*;
class Test {
public static void main(String[] args) {
Function<String, Integer> function = combine(String::length, n -> n * 2);
System.out.println(function.apply("foo"));
}
public static <T1, T2, T3> Function<T1, T3> combine(
Function<T1, T2> first,
Function<T2, T3> second) {
return first.andThen(second);
}
}
Затем вы можете поместить его в класс утилиты и импортировать его статически.
В качестве альтернативы создайте более простой статический метод, который просто возвращает функцию, которую он дал, чтобы компилятор знал, что вы делаете:
import java.util.function.*;
class Test {
public static void main(String[] args) {
Function<String, Integer> function = asFunction(String::length).andThen(n -> n * 2);
System.out.println(function.apply("foo"));
}
public static <T1, T2> Function<T1, T2> asFunction(Function<T1, T2> function) {
return function;
}
}
Ответ 2
Вы можете просто сохранить его в переменной:
Function<String, Integer> toLength = String::length;
Stream.of("ciao", "hola", "hello")
.map(toLength.andThen(n -> n * 2));
Или вы можете использовать актерский состав, но он менее читабельный, IMO:
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length).andThen(n -> n * 2));
Ответ 3
Вы должны иметь возможность добиться того, что хотите встроить, используя приведения:
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length).andThen(n -> n * 2))
Есть только "подсказки типа" для компилятора, поэтому они фактически не "бросают" объект и не имеют накладных расходов на фактический отбор.
В качестве альтернативы вы можете использовать локальную переменную для чтения:
Function<String, Integer> fun = String::length
Stream.of("ciao", "hola", "hello")
.map(fun.andThen(n -> n * 2));
Третий способ, который может быть более кратким, заключается в утилите:
public static <T, X, U> Function<T, U> chain(Function<T, X> fun1, Function<X, U> fun2)
{
return fun1.andThen(fun2);
}
Stream.of("ciao", "hola", "hello")
.map(chain(String::length, n -> n * 2));
Обратите внимание, что это не проверено, поэтому я не знаю, правильно ли работает вывод типа в этом случае.
Ответ 4
Вы также можете использовать
Function.identity().andThen(String::length).andThen(n -> n * 2)
Проблема заключается в том, что String::length
не обязательно a Function
; он может соответствовать многим функциональным интерфейсам. Он должен использоваться в контексте, который предоставляет тип цели, а контекст может быть: присвоением, вызовом метода, литьем.
Если Function
может предоставить статический метод только для целевого ввода, мы могли бы сделать
Function.by(String::length).andThen(n->n*2)
static <T, R> Function<T, R> by(Function<T, R> f){ return f; }
Например, я использую этот метод в функциональном интерфейсе
static <T> AsyncIterator<T> by(AsyncIterator<T> asyncIterator)
Синтаксис сахара для создания AsyncIterator из выражения лямбда или ссылки на метод.
Этот метод просто возвращает аргумент asyncIterator
, который кажется немного странным. Объяснение:
Так как AsyncIterator является функциональным интерфейсом, экземпляр может быть создан с помощью лямбда-выражения или ссылки на метод в трех контекстах:
// Assignment Context
AsyncIterator<ByteBuffer> asyncIter = source::read;
asyncIter.forEach(...);
// Casting Context
((AsyncIterator<ByteBuffer>)source::read)
.forEach(...);
// Invocation Context
AsyncIterator.by(source::read)
.forEach(...);
Третий вариант выглядит лучше, чем два других, и цель этого метода.
Ответ 5
Вы можете использовать литой
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length)
.andThen(n -> n * 2))
.forEach(System.out::println);
печатает
8
8
10
Ответ 6
Вы можете написать:
Stream.of("ciao", "hola", "hello").map(String::length).map(n -> n * 2);
Ответ 7
Мы можем использовать функцию reduce
для объединения нескольких функций в одну.
public static void main(String[] args) {
List<Function<String, String>> normalizers = Arrays.asList(
str -> {
System.out.println(str);
return str;
},
String::toLowerCase,
str -> {
System.out.println(str);
return str;
},
String::trim,
str -> {
System.out.println(str);
return str;
});
String input = " Hello World ";
normalizers.stream()
.reduce(Function.identity(), (a, b) -> a.andThen(b))
.apply(input);
}
Выход:
Hello World
hello world
hello world