Замена связанного вызова метода с использованием ссылки на метод
"Java 8 Lambdas: прагматическое функциональное программирование" имеет пример использования peek
метод в Stream
API. Этот фрагмент кода печатает национальности артистов, чье имя начинается с "The":
Set<Nationality> nationalities = album.getMusician()
.filter(artist -> artist.getName().startsWith("The"))
.map(artist -> artist.getNationality())
.peek(nation -> System.out.println(nation))
.collect(Collectors.toList());
Я хочу переписать этот код со ссылками на методы:
Set<Nationality> nationalities = album.getMusician()
.filter(artist -> artist.getName().startsWith("The"))
.map(Artist::getNationality)
.peek(System.out::println)
.collect(Collectors.toList());
Есть ли решение переписать filter(artist -> artist.getName().startsWith("The"))
?
3 ответа
Вам нужно создать отдельный метод, который принимает Artist и возвращает логическое значение:
private boolean nameStartsWithThe(Artist a) {
return a.getName().startsWith("The");
}
Set<Nationality> nationalities = album.getMusician()
.filter(this::nameStartsWithThe)
или статическим методом:
private static boolean nameStartsWithThe(Artist a) {
return a.getName().startsWith("The");
}
Set<Nationality> nationalities = album.getMusician()
.filter(MyClass::nameStartsWithThe)
Вам нужно что-то, что составляет два метода. Есть несколько методов для составления методов (IntUnaryOperator
имеет compose
а также andThen
методы, которые могут составить два IntUnaryOperator
с новым IntUnaryOperator
). Но те, которые я нашел, кажутся специализированными для определенных типов функциональных интерфейсов; определяющий compose
методы для каждой возможной пары функциональных типов интерфейса были бы слишком громоздкими.
Я получил что-то на работу, которая будет составлять Function
и Predicate
чтобы получить новый Predicate
:
static <T,U> Predicate<T> functionPredicate(Function<T,U> func, Predicate<U> pred) {
return obj -> pred.test(func.apply(obj));
}
То есть он может составлять предикат, который оперирует T
от функции, которая принимает T
и возвращается U
и предикат, который действует на U
, Это будет почти работать на вашем примере, за исключением того, что startsWith
нужен другой параметр. Но это работает:
static boolean startsWithThe(String s) {
return s.startsWith("The");
}
Predicate<Artist> pred = functionPredicate(Artist::getName, ThisClass::startsWithThe);
где ThisClass
какой класс содержит startsWithThe
, Это работает. Если вы хотите избежать написания нового метода (например, startsWithThe
), вы могли бы написать обобщенный метод "параметризованного предиката", чтобы вы
Predicate<Artist> pred = functionPredicate(Artist::getName, parameterizedPredicate(String::startsWith, "The"));
но я не пробовал это.
Поэтому кажется, что можно придумать что-то, что позволит вам использовать ссылки на методы вместо лямбда-выражений. Я спрашиваю, стоит ли это того. Для меня ссылка на метод - это просто сокращение для некоторых видов лямбд; и если вы не можете делать то, что вы хотите, с помощью простой ссылки на метод, я думаю, что использование лямбды является кратким и достаточно ясным, и вам не нужно добавлять всю дополнительную ригмаролу, как мой functionPredicate
метод. Я видел несколько вопросов, которые задают что-то вроде: "Как я могу использовать для этого ссылку на метод вместо лямбды?", И я, честно говоря, не понимаю, почему.
Невозможно заменить эту строку ссылкой на метод.
Ссылка на метод работает с использованием того факта, что во всем лямбда-выражении используется только один объект, и компилятор может вывести его (ссылка не имеет значения, а тип может быть выведен) с помощью целевой типизации.
Так,
artist -> artist.getNationality()
заменяется на
Artist::getNationality
Вот Artist::getNationality
Метод соответствует типу цели, не требуя дополнительной информации.
В случае artist -> artist.getName().startsWith("The")
в лямбда-выражении есть два вызова метода. Порядок, параметры важны, и должны быть указаны.
Похоже, что artist
ссылка должна быть выведена, но компилятор не будет знать, какой объект должен startsWith("The")
метод будет вызван.
Надеюсь это поможет.