是将常规闭包转换为RxJava可以使用的形式的正确方法

问题描述 投票:0回答:2

我刚刚开始考虑将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方法来解决这个问题

groovy closures rx-java
2个回答
3
投票

让我们首先来看一个不带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时,才可以这样做。


0
投票

全部-谢谢您的答复,今天早上我在笔记本电脑上再次尝试时,感觉有点奇怪(而不是在我的台式机上,我遇到了无法将闭包运行时代理投射到使用者界面的运行时错误)

今天早上,我在笔记本电脑上再次尝试了这样的方法,它当然起作用了,并且正常的闭合效果可以按照您的期望变形为SAM接口。

pl.map{num -> num*2}.subscribe({println it} )

所以我现在不确定在桌面上运行的是什么[-必须回去尝试在该计算机上再次进行查看。

因此cfrick / blackdrag,您的响应是正确的-如上所示(Groovy 3.0.2,Rxjava 3.0.0)

[谢谢,Blackdrag扩展了运行时编译器如何生成可扩展抽象Closure的代理innerClass的信息。我应该先浏览一下groovy代码库,看看它是如何工作的。

然而,最终的结果是,我在台式机版本上一定做错了一些事情,这首先使我开始追逐这种鹅。但是我今天早上可以从笔记本电脑上确认正常的SAM强制关闭效果很好。

我想感谢您的快速回复。真的很感激。

PS我检查了.subscribe()方法的IDE选项,并且预期没有varargs args输入-参见下图。

IDE options for subscribe

© www.soinside.com 2019 - 2024. All rights reserved.