kotlin - Kotlin 中使用 `?` 进行类似 Rust 的错误处理,这可能吗?

下面的代码有几个可能的故障。例如,width 可以为 null,或者 r 可以为 false。在所有情况下,我都应该返回一个 result.error() 或类似的东西。

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    if (call.method == "getPlatformVersion") {
      result.success("Android ${android.os.Build.VERSION.RELEASE}")
    } else if (call.method=="registerTexture") {
      val entry: TextureRegistry.SurfaceTextureEntry = texture_registry.createSurfaceTexture();
      val surfaceTexture = entry.surfaceTexture();
      //TODO: return non-sucess when no width and height passed
      val width: Int = call.argument("width")!!
      val height: Int = call.argument("height")!!
      surfaceTexture.setDefaultBufferSize(width, height)
      val response = HashMap<String, Long>()
      RendererPlugin.surfaceTextureMap.put(entry, surfaceTexture)
      val r = RendererPlugin.registerSurfaceTextureNativeHandler(entry.id(), surfaceTexture)
      if (!r) {
        Log.d(LOG_TAG, "attention: failed result from registerSurfaceTextureNativeHandler")
      }
      response.put("textureId", entry.id())
      result.success(response)
    }
  }

在 Rust 上,我会将所有这些变成一个导致 Result<(), Error> 的闭包,然后在 onMethodCall 内执行闭包,如果出现错误,我会返回一个错误。此外,闭包将充满以 ? 结尾的调用,因此它会自动返回具有转换为 ErrorFrom<> 实现的错误。

如何在 Kotlin 中有效地做到这一点?有没有办法在这个闭包中创建一个闭包并轻松返回成功或错误,然后根据这个结果我调用 result.sucessresult.error

回答1

有一个 runCatching 函数可以调用任何东西。在以下 lambda 闭包中,您调用它的对象是“this”。你可以为成功返回一些东西,也可以为失败抛出一些东西。然后您可以将此结果解压缩到您的 Result 对象中。 error()require()check() 是在不中断代码流的情况下在特定条件下抛出的有用方法。

要从 lambda 封装返回某些内容,您可以使用 return@nameOfFunctionLambdaIsPassedTo 手动执行此操作,或者让 lambda 的最后一个表达式为 return value。 when 表达式可以用作 lambda 的最后一个(唯一)表达式,因此 when 的每个分支中的最后一件事是成功返回的结果(或抛出的错误)。

这是一个例子:

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    call.runCatching {
        when(method) {
            "getPlatformVersion" -> "Android ${android.os.Build.VERSION.RELEASE}"
            "registerTexture" -> {
                 val entry = texture_registry.createSurfaceTexture()
                 val surfaceTexture = entry.surfaceTexture()
                 val width: Int = argument("width") ?: error("no width passed")
                 val height: Int = argument("height") ?: error("no height passed")
                 surfaceTexture.setDefaultBufferSize(width, height)
                 RendererPlugin.surfaceTextureMap.put(entry, surfaceTexture)
                 check(RendererPlugin.registerSurfaceTextureNativeHandler(entry.id(), surfaceTexture)) {
                     "attention: failed result from registerSurfaceTextureNativeHandler"
                 }
                 mapOf("textureId", entry.id())
            }
            else -> error("unsupported method name: $method")
        }
    }
        .onSuccess(result::success)
        .onFailure(result::failure)
}

如果你想避免调用 onSuccess/onFailure,你可以创建一个通用的扩展函数,你可以像这样重复使用:

inline fun MethodCall.runForResult(result: Result, block: MethodCall.() -> Any) {
    runCatching(block).onSuccess(result::success).onFailure(result::failure)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    call.runForResult(result) {
        when(method) {
            "getPlatformVersion" -> "Android ${android.os.Build.VERSION.RELEASE}"
            "registerTexture" -> {
                 val entry = texture_registry.createSurfaceTexture()
                 val surfaceTexture = entry.surfaceTexture()
                 val width: Int = argument("width") ?: error("no width passed")
                 val height: Int = argument("height") ?: error("no height passed")
                 surfaceTexture.setDefaultBufferSize(width, height)
                 RendererPlugin.surfaceTextureMap.put(entry, surfaceTexture)
                 check(RendererPlugin.registerSurfaceTextureNativeHandler(entry.id(), surfaceTexture)) {
                     "attention: failed result from registerSurfaceTextureNativeHandler"
                 }
                 mapOf("textureId", entry.id())
            }
            else -> error("unsupported method name: $method")
        }
    }
}

回答2

我希望这可以帮助你。

注意:我假设 result.success() 接受 Any 因为你返回 StringHashMap 作为参数,但 kotlin.Result 没有几个通用的成功参数。您可以使用 kotlin.Pair<A, B> 作为 kotlin.Result 的成功类型:D

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    val finalResult: kotlin.Result<Any> = call.run {
        if (method == "getPlatformVersion") kotlin.Result.success("Android ${android.os.Build.VERSION.RELEASE}")
        else if (method == "registerTexture") {
            val entry: TextureRegistry.SurfaceTextureEntry = texture_registry.createSurfaceTexture();
            val surfaceTexture = entry.surfaceTexture();
            val width = argument("width") ?: return@run kotin.Result.failure(IllegalArgumentException("width is null"))
            val height = argument("height") ?: return@run kotlin.Result.failure(IllegalArgumentException("height is null"))

            surfaceTexture.setDefaultBufferSize(width, height)
            val response = HashMap<String, Long>()
            RendererPlugin.surfaceTextureMap.put(entry, surfaceTexture)
            val r = RendererPlugin.registerSurfaceTextureNativeHandler(entry.id(), surfaceTexture)
            if (!r) return@run kotlin.Result.failure(IllegalArgumentException("r is false"))
            response.put("textureId", entry.id())
            kotlin.Result.success(response)
        }
        else kotlin.Result.failure(IllegalStateException("undefined call.method"))
    }

    with (finalResult) {
        onSuccess(result::success)
        onFailure(result::fail)
    }
}

相似文章