Kotlin:如何将具有可变参数的函数作为参数传递给其他函数

时间:2019-09-26 15:03:13

标签: android generics kotlin retrofit

因此,我正在重写应用程序的代码,使其“干净”(按照Android团队推荐的MVVM模式,进行层分离)

在这里,我有一个简单的Retrofit接口可以与我的API通信

interface Api {

    @GET("comments")
    suspend fun getPlaceComments(@Query("placeId") placeId: String): Response<List<CommentResponse>>

    @POST("comments")
    suspend fun addPlaceComment(@Header("placeId") placeId: String, @Header("text") text: String): Response<Unit>

    @DELETE("comments")
    suspend fun deletePlaceComment(@Header("placeId") placeId: String): Response<Unit>
}

只是一个简单的CRUD。

现在,向上一层,我有了我的SocialRepository。为了避免代码重复,我创建了通用方法callSafely,该方法将一个正在挂起的API函数和一个placeId作为其参数。

class SocialRepository {
    private val client: Api = ApiClient.webservice

    private suspend fun <T> callSafely(
        apiMethod: suspend (placeId: String) -> Response<T>,
        placeId: String,
    ): T? {
        Log.d(TAG, "$apiMethod called safely")

        var response: Response<T>? = null

        try {
            response = apiMethod(placeId)
        } catch (e: Exception) {
            e.printStackTrace()
        }

        if (response?.isSuccessful != true) {
            Log.w(TAG, "response.isSuccessful isn't true.")
        }

        return response?.body()
    }

    suspend fun getPlaceComments(placeId: String): List<CommentResponse>? {
        return callSafely(client::getPlaceComments, placeId)
    }

    suspend fun deletePlaceComment(placeId: String): Unit? {
        return callSafely(client::deletePlaceComment, placeId)
    }

    suspend fun addPlaceComment(placeId: String, text: String): Unit? {
        return callSafely(client::addPlaceComment, placeId, text) // HERE LIES THE PROBLEM
        // I can't pass additional data because the method signature won't match with what's defined in callSafely()
    }
}

现在,它运行良好,当然我也有Activity和它的ViewModel,并且ViewModel在存储库中调用了一个方法,等等。

重要的是,添加位置注释需要其他数据,例如注释的实际文本。获取和删除注释仅需要placeId,而添加注释时,其内容也需要text。 我读过在Kotlin中无法传递vararg函数。我也不想用List of params之类的东西弄乱所有API方法,在大多数情况下,这些方法将是空的,只会造成混乱。

我可以采用简单的方法,只需将callSafely的代码复制到addPlaceComment并进行更改,但这不是我想要的。我知道如何解决问题,但我不知道如何解决the clean way。将来我可能会添加一些需要更多数据的端点(placeId除外),问题将再次出现。

在这种情况下您会怎么做?怎么写“正确的方式”?

我什至不知道如何正确表达我要寻找的东西,这就是为什么这篇文章如此繁琐。对不起,提前。我真的希望你能帮助我。

2 个答案:

答案 0 :(得分:4)

“干净的方式”是非常广泛的概念。一切都取决于您的需求,没有“做事的一种好方法”。

在您的特定情况下,您有几种选择:

1)类型别名

@array = <STDIN>;
@sorted = sort { $a <=> $b } @array ;
for $i(@sorted)
{
    $cnt =0;
    for $j(@sorted)
    {
        if($i eq $j)
        {
            $cnt = $cnt + 1;
            $data{$i}= $cnt;
        }
    }
}
@modes = sort { $data{$a} <=> $data{$b} } keys %data;
$mode = $modes[-1];

2)Varargs

typealias ApiCall1<P, R> = suspend (P) -> Response<R>
typealias ApiCall2<P1, P2, R> = suspend (P1, P2) -> Response<R>

fun <P> callSafely(param: P, call: ApiCall1<P, YourResult>): YourResult
fun <P1, P2> callSafely(param1: P1, param2: P2, call: ApiCall2<P1, P2, YourResult>): YourResult

3)Lambdas (根据您的情况而定)

没有人强迫您使用方法引用。在需要时使用lambda。但是将lambda放置为“更清洁”代码的最后一个参数。

fun callSafely(vararg params: String, call: suspend (arr: Array<String>) -> YourResult {
   ...
   call(*params) 
   ...
}

答案 1 :(得分:1)

尝试一下:

class SocialRepository {
private val client: Api = ApiClient.webservice

private suspend fun <T> callSafely(
    apiMethod: suspend (placeId: String) -> Response<T>,
    vararg stringParams: String,
): T? {
    Log.d(TAG, "$apiMethod called safely")

    var response: Response<T>? = null

    try {
        response = apiMethod(stringParams[0])
    } catch (e: Exception) {
        e.printStackTrace()
    }

    if (response?.isSuccessful != true) {
        Log.w(TAG, "response.isSuccessful isn't true.")
    }

    return response?.body()
}

suspend fun getPlaceComments(placeId: String): List<CommentResponse>? {
    return callSafely(apiMethod= client::getPlaceComments, stringParams=*arrayOf(placeId))
}

suspend fun deletePlaceComment(placeId: String): Unit? {
    return callSafely(apiMethod=client::deletePlaceComment, stringParams=*arrayOf(placeId))
}

suspend fun addPlaceComment(placeId: String, text: String): Unit? {
    return callSafely(apiMethod = client::addPlaceComment,stringParams= *arrayOf(placeId,text))
}

}