Ссылки на метод
Как мы все знаем к настоящему времени, мы можем использовать ссылки на методы, такие как String::isEmpty
, в Java 8, чтобы ссылаться на метод, который используется, когда мы, например, передаем по элементам поток. Посмотрите на этот фрагмент кода:
1
|
Stream.of( "A" , "" , "B" ).filter(Stream::isEmpty).count(); |
который даст результат 1 (потому что в потоке есть только один пустой элемент). Но если мы хотим отфильтровать непустые строки, нам нужно написать .filter(s -> !s.isEmpty())
который является лямбда- .filter(s -> !s.isEmpty())
. Очевидно, здесь есть раздражающая асимметрия. Мы можем использовать ссылку на метод, но не на его отрицание. Мы можем написать predicate.negate()
но не можем написать Stream::isEmpty.negate()
или !Stream::isEmpty
.
Почему это? Это потому, что ссылка на метод не является лямбда или функциональным интерфейсом. Однако Ссылка на метод может быть преобразована в один или несколько Функциональных Интерфейсов, используя вывод типа Java. Наш пример String::isEmpty
может быть разрешен как минимум:
-
Predicate<String>
-
Function<String, Boolean>
Итак, нам нужно как-то разрешить все потенциальные неоднозначности и решить, в какой функциональный интерфейс мы хотим превратить ссылку на метод. Прочтите этот пост и узнайте, как частично решить эту проблему. Я использовал код, представленный здесь в проекте с открытым исходным кодом Speedment, который делает базы данных похожими на потоки Java 8. Не стесняйтесь попробовать Speedment.
Ускорение также содержит построители предикатов, которые позволяют вам использовать функции, такие как Entity.NAME::isEmpty и Entity.NAME::isNotEmpty напрямую.
Разрешение метода Ссылки
Проблема может быть частично исправлена путем введения некоторого «соединения» в форме статических методов, которое принимает ссылку на метод и возвращает ее как представление определенного функционального интерфейса. Рассмотрим этот короткий статический метод:
1
2
3
|
public static <T> Predicate<T> as(Predicate<T> predicate) { return predicate; } |
Теперь, если мы импортируем этот метод статически, мы на самом деле можем использовать ссылку на метод проще, как показано в этом коротком примере:
1
|
Stream.of( "A" , "" , "B" ).filter(as(String::isEmpty).negate()).count(); |
Код вернет 2, которое является числом непустых элементов в потоке. Это шаг вперед с точки зрения использования метода. Другое преимущество состоит в том, что это решение позволяет нам легче составлять наши предикаты, например:
1
|
.filter(as(String::isEmpty).negate().and( "A" ::equals)) |
Разрешение всех ссылок на метод
Но есть еще проблема, которую мы должны решить. Мы не можем просто начать создавать много статических функций as()
, потому что ссылка на метод может быть разрешена для нескольких типов потенциальных методов as()
же, как указано в начале этого поста. Таким образом, лучшим подходом является добавление имени типа функционального интерфейса к каждому статическому методу, что позволяет нам программно выбирать конкретный метод Ссылка на метод преобразования функционального интерфейса. Вот вспомогательный класс, который позволяет преобразовывать ссылки на методы в любой соответствующий функциональный интерфейс, который находится в стандартном Java-пакете java.util.function
.
- Загрузите последнюю версию прямо с GitHub здесь
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
|
import java.util.function.*; /** * * @author Per Minborg */ public class FunctionCastUtil { public static <T, U> BiConsumer<T, U> asBiConsumer(BiConsumer<T, U> biConsumer) { return biConsumer; } public static <T, U, R> BiFunction<T, U, R> asBiFunction(BiFunction<T, U, R> biFunction) { return biFunction; } public static <T> BinaryOperator<T> asBinaryOperator(BinaryOperator<T> binaryOperator) { return binaryOperator; } public static <T, U> BiPredicate<T, U> asBiPredicate(BiPredicate<T, U> biPredicate) { return biPredicate; } public static BooleanSupplier asBooleanSupplier(BooleanSupplier booleanSupplier) { return booleanSupplier; } public static <T> Consumer<T> asConsumer(Consumer<T> consumer) { return consumer; } public static DoubleBinaryOperator asDoubleBinaryOperator(DoubleBinaryOperator doubleBinaryOperator) { return doubleBinaryOperator; } public static DoubleConsumer asDoubleConsumer(DoubleConsumer doubleConsumer) { return doubleConsumer; } public static <R> DoubleFunction<R> asDoubleFunction(DoubleFunction<R> doubleFunction) { return doubleFunction; } public static DoublePredicate asDoublePredicate(DoublePredicate doublePredicate) { return doublePredicate; } public static DoubleToIntFunction asDoubleToIntFunction(DoubleToIntFunction doubleToIntFunctiontem) { return doubleToIntFunctiontem; } public static DoubleToLongFunction asDoubleToLongFunction(DoubleToLongFunction doubleToLongFunction) { return doubleToLongFunction; } public static DoubleUnaryOperator asDoubleUnaryOperator(DoubleUnaryOperator doubleUnaryOperator) { return doubleUnaryOperator; } public static <T, R> Function<T, R> asFunction(Function<T, R> function) { return function; } public static IntBinaryOperator asIntBinaryOperator(IntBinaryOperator intBinaryOperator) { return intBinaryOperator; } public static IntConsumer asIntConsumer(IntConsumer intConsumer) { return intConsumer; } public static <R> IntFunction<R> asIntFunction(IntFunction<R> intFunction) { return intFunction; } public static IntPredicate asIntPredicate(IntPredicate intPredicate) { return intPredicate; } public static IntSupplier asIntSupplier(IntSupplier intSupplier) { return intSupplier; } public static IntToDoubleFunction asIntToDoubleFunction(IntToDoubleFunction intToDoubleFunction) { return intToDoubleFunction; } public static IntToLongFunction asIntToLongFunction(IntToLongFunction intToLongFunction) { return intToLongFunction; } public static IntUnaryOperator asIntUnaryOperator(IntUnaryOperator intUnaryOperator) { return intUnaryOperator; } public static LongBinaryOperator asLongBinaryOperator(LongBinaryOperator longBinaryOperator) { return longBinaryOperator; } public static LongConsumer asLongConsumer(LongConsumer longConsumer) { return longConsumer; } public static <R> LongFunction<R> asLongFunction(LongFunction<R> longFunction) { return longFunction; } public static LongPredicate asLongPredicate(LongPredicate longPredicate) { return longPredicate; } public static <T> LongSupplier asLongSupplier(LongSupplier longSupplier) { return longSupplier; } public static LongToDoubleFunction asLongToDoubleFunction(LongToDoubleFunction longToDoubleFunction) { return longToDoubleFunction; } public static LongToIntFunction asLongToIntFunction(LongToIntFunction longToIntFunction) { return longToIntFunction; } public static LongUnaryOperator asLongUnaryOperator(LongUnaryOperator longUnaryOperator) { return longUnaryOperator; } public static <T> ObjDoubleConsumer<T> asObjDoubleConsumer(ObjDoubleConsumer<T> objDoubleConsumer) { return objDoubleConsumer; } public static <T> ObjIntConsumer<T> asObjIntConsumer(ObjIntConsumer<T> objIntConsumer) { return objIntConsumer; } public static <T> ObjLongConsumer<T> asObjLongConsumer(ObjLongConsumer<T> objLongConsumer) { return objLongConsumer; } public static <T> Predicate<T> asPredicate(Predicate<T> predicate) { return predicate; } public static <T> Supplier<T> asSupplier(Supplier<T> supplier) { return supplier; } public static <T, U> ToDoubleBiFunction<T, U> asToDoubleBiFunction(ToDoubleBiFunction<T, U> toDoubleBiFunction) { return toDoubleBiFunction; } public static <T> ToDoubleFunction<T> asToDoubleFunction(ToDoubleFunction<T> toDoubleFunction) { return toDoubleFunction; } public static <T, U> ToIntBiFunction<T, U> asToIntBiFunction(ToIntBiFunction<T, U> toIntBiFunction) { return toIntBiFunction; } public static <T> ToIntFunction<T> asToIntFunction(ToIntFunction<T> ioIntFunction) { return ioIntFunction; } public static <T, U> ToLongBiFunction<T, U> asToLongBiFunction(ToLongBiFunction<T, U> toLongBiFunction) { return toLongBiFunction; } public static <T> ToLongFunction<T> asToLongFunction(ToLongFunction<T> toLongFunction) { return toLongFunction; } public static <T> UnaryOperator<T> asUnaryOperator(UnaryOperator<T> unaryOperator) { return unaryOperator; } private FunctionCastUtil() { } } |
Поэтому после того, как мы статически импортировали соответствующие методы, мы можем написать:
1
|
Stream.of( "A" , "" , "B" ).filter(asPredicate(String::isEmpty).negate()).count(); |
Еще лучшее решение
Было бы еще лучше, если бы все функциональные интерфейсы сами содержали статический метод, который мог бы взять подходящую ссылку на метод и превратить ее в типизированный функциональный интерфейс. Например, стандартный функциональный интерфейс Java Predicate
будет выглядеть следующим образом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
@FunctionalInterface public interface Predicate<T> { boolean test(T t); default Predicate<T> and(Predicate<? super T> other) {...} default Predicate<T> negate() {...} default Predicate<T> or(Predicate<? super T> other) {...} static <T> Predicate<T> isEqual(Object targetRef) {...} // New proposed support method to return a // Predicate view of a Functional Reference public static <T> Predicate<T> of(Predicate<T> predicate) { return predicate; } } |
Это позволило бы нам написать:
1
|
Stream.of( "A" , "" , "B" ).filter(Predicate.of(String::isEmpty).negate()).count(); |
Что лично мне кажется, выглядит хорошо!
Свяжитесь с ближайшим разработчиком Open JDK и внесите предложение!
Ссылка: | Поместите ваши ссылки на методы Java 8 на работу от нашего партнера JCG Пера Минборга в блоге Minborg по Java Pot . |