Что такое циклоп-реакция?
Появление лямбда-выражений и методов по умолчанию в Java 8 ознаменовало самые большие структурные изменения в языке Java за последние десять лет. Вдобавок к этому были добавлены некоторые новые классные API-интерфейсы, такие как Stream, Optional, CompletableFuture — наконец, разработчики Java могли Stream, Optional, CompletableFuture код в более функциональном стиле. Хотя это было очень кстати, для многих улучшений не хватило.
Stream, Optional, CompletableFuture имеют общую абстрактную структуру и подчиняются одним и тем же правилам. Тем не менее, API не согласуются с общими именами методов, не говоря уже о предоставлении общего интерфейса. Например, Stream#map / Optional#map становится CompletableFuture#thenApply . Кроме того, функциональность, добавленная в Stream & Optional вообще отсутствует в коллекциях. Где находится List#map ?
Реализация JDK Stream работает хорошо, полностью ленива и хорошо разработана для расширения, но предоставляет только ограниченное подмножество потенциальных операторов (возможно, ограниченное фокусом на параллелизме данных). В пустые ступенчатые библиотеки, такие как jOOλ с его последовательным расширением Stream (называемым Seq ). Seq добавляет много дополнительных потоковых операторов. В jOOλ обычно добавлено множество отсутствующих функциональных возможностей, таких как Tuples.
Основная цель циклоп-реакции , а также добавления оригинальных функций, таких как FutureStreams , заключается в предоставлении механизма для объединения как JDK API, так и сторонних функциональных библиотек. После запуска Java 8 появился кембрийский взрыв классных библиотек. Такие библиотеки, как Javaslang & Project Reactor . Циклоп-реакция делает это в первую очередь, расширяя JDK и используя другие библиотеки, такие как jOOλ , pCollections & Agrona . Эти библиотеки, в свою очередь, также расширяют интерфейсы JDK, где это возможно, для добавления функций, таких как постоянные коллекции, и ожидания без очереди для многих производителей.
Помимо повторного использования и расширения интерфейсов JDK, наши цели заключались в том, чтобы облегчить разработчикам интеграцию с внешними библиотеками, используя сторонние стандарты, такие как API реактивных потоков, и создавая собственные абстракции там, где не существует установленного стандарта. В настоящее время мы специализируемся на интеграции с библиотеками Google: Guava, RxJava, Functional Java, Project Reactor и Javaslang . Мы создали абстракции для обтекания типов, таких как Stream, Optional & CompletableFuture — там, где ранее не было или не было интерфейса. Мы выбрали эти цели, потому что мы используем циклопическое реагирование в производственной среде в архитектуре микросервисов, и нам важно использовать правильную технологию для решения проблемы и обеспечить ее плавную интеграцию с остальной частью нашей базы кода.
Циклоп-реакция — довольно большой многофункциональный проект, кроме того, он имеет ряд модулей интеграции . В статье ниже я расскажу о некоторых доступных функциях с конкретной целью показать, как циклопическая реакция помогает объединить точки в JDK и войти в дивный новый мир динамичного сообщества Java 8 с открытым исходным кодом.
Расширение JDK
циклоп-реакция расширяет API JDK, где это возможно. Например, ReactiveSeq добавляет функциональность для обработки ошибок, асинхронной обработки и многое другое расширяет как JDK Stream, так и Seq в jOOλ. Циклоп-реагирование Расширения коллекции, вместо создания новых реализаций коллекции, реализуют и расширяют соответствующие интерфейсы JDK. cyclops-реагировать LazyFutureStream в свою очередь, расширяет ReactiveSeq и позволяет агрегировать операции над потоками фьючерсов, как если бы это был простой поток (это оказывается очень полезным для асинхронной и производительной обработки большого количества типичных операций ввода-вывода Java).
ListX расширяет List , но добавляет операторы, которые выполняются с нетерпением
|
1
2
|
ListX<Integer> tenTimes = ListX.of(1,2,3,4) .map(i->i*10); |
Циклоп-реакция добавляет множество операторов для пользователей, чтобы исследовать. Мы можем, например, применять функции для нескольких коллекций одновременно
API реактивных потоков действует как естественный мост между производителями (издателями) данных и потребителями (подписчиками). Все типы данных Циклоп-Реакция реализуют интерфейс Publisher из реактивных потоков, также предоставляются реализации Subscriber которые могут конвертировать в любой тип Циклоп-Реакция. Это упрощает прямую интеграцию с другими библиотеками на основе реактивных потоков, такими как Project Reactor.
Например, мы можем лениво заполнять Reactor Flux от любого издателя циклопов, такого как SortedSetX , или заполнять тип реагирования циклопов из типа Reactor.
|
1
2
3
4
5
6
|
Flux<Integer> stream = Flux.from( SortedSetX.of(1,2,3,4,5,6,7,8));//Flux[1,2,3,4,5,6,7,8]ListX<Character> list = ListX.fromPublisher( Flux.just("a","b","c")); |
Типы Reactor Flux и Mono могут работать напрямую с циклопами-реакциями For понимания (каждая поддерживаемая библиотека также имеет свой собственный набор собственных классов For понимания в своем модуле интеграции).
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
// import static com.aol.cyclops.control.For.*; Publishers.each2( Flux.just(1,2,3), i -> ReactiveSeq.range(i,5),Tuple::tuple).printOut(); /*(1, 1)(1, 2)(1, 3)(1, 4)(2, 2)(2, 3)(2, 4)(3, 3)(3, 4)*/ |
For понимания — это способ управления вложенными итерациями над типами с помощью методов flatMap и map путем каскадного обращения к соответствующим методам. В циклоп-реакции вложенные операторы могут получить доступ к элементам предыдущих операторов, поэтому For понимания может быть очень полезным способом управления поведением существующих. Например, чтобы гарантировать, что вызовы существующих методов findId и loadData, которые могут возвращать нулевые значения, и будут выбрасывать NPE, если они снабжены нулевым параметром, мы можем использовать For compception, который безопасно выполнит loadData только при возврате Optional со значением. от findId ()
|
1
2
3
4
|
List<Data> data = For.optional(findId()) .optional(this::loadData);//loadData is only called if findId() returns a value |
Аналогично, такой тип, как Try, можно использовать для обработки исключительных результатов из findId или loadData, Futures можно использовать для асинхронного выполнения цепочечных методов и так далее.
Создание межбиблиотечных абстракций
Java 8 представила Monads для Java ( Stream, Optional, CompletableFuture ), но не предоставила общего интерфейса, который помог бы использовать повторно, фактически имена методов, используемые в CompletableFuture значительно отличаются от имен, используемых в Optional & Stream для той же функции. Таким образом, map стала тогда thenApply и flatMap thenCompose . В мире Java 8 монады становятся все более распространенной моделью, но зачастую нет возможности абстрагироваться от них. В циклоп-реакции вместо того, чтобы пытаться определить интерфейс для представления монад, мы создали набор интерфейсов-оболочек и несколько пользовательских адаптеров для адаптации различных экземпляров основных библиотек функционального стиля для Java 8 к этим оболочкам. AnyM расширяют AnyM (сокращение от Any Monad), и есть два подчиненных интерфейса — AnyMValue представляющий любой монадический тип, который разрешается в одно значение (например, Optional или CompletableFuture ), или AnyMSeq который в конечном итоге разрешается в последовательность значений (например, Stream). или список). Оболочки расширения cyclops предоставляют механизм для переноса типов из RxJava, Guava, Reactor, FunctionalJava и Javaslang.
|
1
2
3
4
5
6
7
|
//We can wrap any type from Reactor, RxJava,//FunctionalJava, Javaslang, GuavaAnyMSeq<Integer> wrapped = Fj.list(List.list(1,2,3,4,5));//And manipulate itAnyMSeq<Integer> timesTen = wrapped.map(i->i*10); |
Циклоп-реакция предоставляет общий набор интерфейсов, от которых наследуются эти оболочки (и другие типы Циклоп-реагирования), позволяя разработчикам писать более универсальный повторно используемый код. AnyM расширяет возможности AnyM реактивных потоков, то есть вы можете сделать любой тип Javaslang, Guava, FunctionalJava или RxJava издателем реактивных потоков с помощью cyclops-реагировать.
|
1
2
3
4
5
6
7
8
9
|
AnyMSeq<Integer> wrapped = Javaslang.traversable(List.of(1,2,3,4,5));//The wrapped type is a reactive-streams publisherFlux<Integer> fromJavaslang = Flux.from(wrapped);wrapped.forEachWithError( System.out::println, System.out::err); |
Кроме того, реактивная функциональность от циклопа-реактива предоставляется непосредственно на типах AnyM. Это означает, что мы можем, например, запланировать передачу данных из потока Javaslang или FunctionalJava — или выполнить операцию уменьшения медленно или асинхронно.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
AnyMSeq<Integer> wrapped = Javaslang.traversable(Stream.of(1,2,3,4,5));CompletableFuture<Integer> asyncResult = wrapped.futureOperations(Executors.newFixedThreadPool(1)) .reduce(50, (acc, next) -> acc + next);//CompletableFuture[1550]AnyMSeq<Integer> wrapped = FJ.list(list.list(1,2,3,4,5));Eval<Integer> lazyResult = wrapped.map(i -> i * 10) .lazyOperations() .reduce(50, (acc,next) -> acc + next);//Eval[15500]HotStream<Integer> emitting = wrapped.schedule( "0 * * * * ?", Executors.newScheduledThreadPool(1));emitting.connect() .debounce(1,TimeUnit.DAYS) .forEachWithError( this::logSuccess, this::logFailure); |
Существует много возможностей для изучения как циклопов-реакции, так и новой более широкой экосистемы Java 8, и, надеюсь, у вас получится увлекательное приключение, в котором вы сможете поиграть, изучить и расширить границы Java 8!
| Ссылка: | Cyclops-Reaction Организует кембрийский взрыв библиотек Java 8 от нашего партнера по JCG Лукаса Эдера в блоге JAVA, SQL и AND JOOQ . |