Я только начал смотреть на использование Rx Java V3 с Groovy v3. Естественно, я склоняюсь к тому, чтобы начать с Closures.
Но метод Rx Java subscribe () не принимает их. Поэтому я реализовал конкретный класс FunctionalClosure, который действует как преобразование из замыкания в Rx java Consumer, или Function или MethodClosure, как показано ниже.
В чем я не уверен, так это в том, что это идиоматически Лучший способ решить мою проблему. Частично проблема заключается в том, что Groovy Closure является абстрактным классом, поэтому вы должны расширить его. Во-вторых, единственный способ, которым я мог найти «хранение» представленного замыкания, - это иметь ссылку на Замыкание внутри этой конкретной реализации.
Что я не могу понять, так это когда вы пишете Closure myClos = {xx -> ...}
в редакторе для создания замыкания, где хранится этот исполняемый код? Неявным образом я хотел бы сохранить свой клон входного замыкания в том же месте - но, глядя на код абстрактного класса Closure, я не могу понять, где это.
Вы должны реализовать doCall ()
метод в вашем конкретном классе, который я сделал, и он просто вызвал внутреннюю переменную закрытия действия
import groovy.transform.InheritConstructors
import io.reactivex.rxjava3.functions.Consumer
import io.reactivex.rxjava3.functions.Function
import org.codehaus.groovy.runtime.MethodClosure
/**
* class to wrap a closure and convert it into a RxJava Consumer
* @param <T> expected type of the arg that that the closure will be called with
*/
@InheritConstructors
class FunctionalClosure<T, R> extends Closure implements Consumer<T>, Function<T,R> {
private Closure action = {}
//maximumNumberOfParameters = 1
//parameterTypes = EMPTY_CLASS_ARRAY
FunctionalClosure() {
super(null)
}
FunctionalClosure (final Closure clos) {
//setup the abstract closure with the owner of the closure
//super(clos?.owner)
super (clos.clone())
maximumNumberOfParameters = clos.getMaximumNumberOfParameters()
action = clos.clone()
}
//implement doCall to direct the call() to the action closure
protected Object doCall(Object arguments) {
return action(arguments)
}
Closure<T> leftShift (final Closure clos) {
action = clos.clone()
}
/**
* as we have an embedded action closure, make sure when setting the closure delegate
* that this is set on the action.
* @param delegate - the object you want to provide the context for the action
*/
//
void setDelegate (Object delegate) {
action.delegate = delegate
}
/**
* implements the RxJava Consumer contract, takes a generic arg of type T,
* an invokes the closure call () with the arg
* @param arg
*/
void accept (T arg) {
call (arg)
}
/**
* implements the RxJava Function contract, takes a generic arg of type T,
* an invokes the closure call () with the arg, and returns the result of the call
* @param arg
*/
R apply (T arg) {
return call (arg)
}
/**
* static from method, accepts a closure and assigns a clone of it
* and returns result as Consumer<T>
* @param clos pass some closure to convert to Functional type
* @return Consumer<T>
*/
static <T> Consumer<T> consumerFrom (Closure clos ) {
assert clos
if (clos.maximumNumberOfParameters == 0){
throw new IncorrectClosureArgumentsException("from: closure must accept at least one argument")
}
Closure cons = new FunctionalClosure<>(clos.clone())
cons
}
/**
* static from method, accepts a closure and assigns a clone of it
* and returns result as Function<T, R>
* @param clos pass some closure to convert to Functional type
* @return Consumer<T>
*/
static <T,R> Function<T, R> functionFrom (Closure clos ) {
assert clos
if (clos.maximumNumberOfParameters == 0){
throw new IncorrectClosureArgumentsException("from: closure must accept at least one argument")
}
Closure cons = new FunctionalClosure<>(clos.clone())
cons
}
/**
* static from method, accepts a closure and assigns a clone of it
* and returns result as Consumer<T>
* @param clos pass some closure to convert to Functional type
* @return Consumer<T>
*/
static MethodClosure asMethodClosure (Closure clos ) {
assert clos
Closure cons = new FunctionalClosure<>(clos.clone())
cons::accept
}
}
Кажется, все это работает, то есть я могу написать такой скрипт, и он напечатает все числа, когда я подпишусь на одного из потребителей, или я использую метод stati c FunctionalClosure.asMethodClosure {println it}
.
Consumer cons = new FunctionalClosure ()
cons << {println it}
Flowable pl = Flowable.fromIterable([1,2,3])
//Function pc = {num -> println num} as Function
pl.map{num -> num*2}.subscribe(cons)
Я надеюсь, что кто-то скажет: это правильный 'способ решения этой проблемы, или же на самом деле это лучший идиоматический c Groovy способ сделать это.