我刚刚开始考虑将RxJava V3与Groovy v3结合使用。自然,我倾向于从闭包开始。
但是RxJava的subscription()方法不接受它们。因此,我实现了一个FunctionalClosure具体类,它充当从闭包到Rxjava Consumer或Function或MethodClosure的转换,如下所示。
我不确定,这是习惯上解决我问题的“最佳” Groovy方法。问题的一部分是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
}
}
这一切似乎都有效,也就是说,我可以编写这样的脚本,并且在我与消费者订阅或是否使用静态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)
我希望的是有人可以说'这是正确的'方法来解决这个问题,或者实际上是否有更好的惯用Groovy方法来解决这个问题
让我们首先来看一个不带FunctionalClosure的代码示例:
@Grapes(
@Grab(group='io.reactivex.rxjava3', module='rxjava', version='3.0.0')
)
import io.reactivex.rxjava3.functions.*
import io.reactivex.rxjava3.core.*
Flowable pl = Flowable.fromIterable([1,2,3])
pl.map {num -> num*2}.subscribe {num -> println num}
这使用的是Groovy 3.0.1,可以很好地接受subscribe方法的Closure参数。我也将这称为惯用方式。
如果确实是您要保留的班次:
@Grapes(
@Grab(group='io.reactivex.rxjava3', module='rxjava', version='3.0.0')
)
import io.reactivex.rxjava3.functions.*
import io.reactivex.rxjava3.core.*
def mul2 = {num -> num*2}
def add1 = {num -> num+1}
def add1AndDouble = add1>>mul2
Flowable pl = Flowable.fromIterable([1,2,3])
pl.map (add1AndDouble).subscribe {it -> println it}
或如果需要左移,则使用mul2<<add1
。
自“该可执行代码存储在哪里?”主要的答案是Groovy将创建一个扩展Closure的内部类。当前类或脚本的内部类(当然不会更改Closure)将具有doCall方法,如果在Closure上调用“ call”方法,则该方法将被调用。如果使用的是“ MethodClosure”,则代码将存储在您引用的方法中,而Closure只是该方法调用的调用者前端。在Groovy 3中,越来越多的代码将使用Java 8+来实现这一点,并产生一个invokedynamic调用。然后将代码存储在当前类/脚本的方法中。
如果使用Groovy编写代码,通常根本不需要扩展Closure。仅当您要在Groovy中调用某个需要关闭的东西并且您的调用代码不是Groovy时,才可以这样做。
全部-谢谢您的答复,今天早上我在笔记本电脑上再次尝试时,感觉有点奇怪(而不是在我的台式机上,我遇到了无法将闭包运行时代理投射到使用者界面的运行时错误)
今天早上,我在笔记本电脑上再次尝试了这样的方法,它当然起作用了,并且正常的闭合效果可以按照您的期望变形为SAM接口。
pl.map{num -> num*2}.subscribe({println it} )
所以我现在不确定在桌面上运行的是什么[
[谢谢,Blackdrag扩展了运行时编译器如何生成可扩展抽象Closure的代理innerClass的信息。我应该先浏览一下groovy代码库,看看它是如何工作的。
然而,最终的结果是,我在台式机版本上一定做错了一些事情,这首先使我开始追逐这种鹅。但是我今天早上可以从笔记本电脑上确认正常的SAM强制关闭效果很好。
我想感谢您的快速回复。真的很感激。
PS我检查了.subscribe()方法的IDE选项,并且预期没有varargs args输入-参见下图。