如何在不使用Java的纯Kotlin中实现接口null与throw调度

来源:爱站网时间:2021-09-16编辑:网友分享
请考虑以下示例:CommonHandler.java:带有附加逻辑的公共处理程序。在这里,它非常简化:import kotlin.jvm.functions.Function0;公共类CommonHandler {private void ...

问题描述


考虑此示例:


CommonHandler.java

具有附加逻辑的通用处理程序。这是非常简化的:

import kotlin.jvm.functions.Function0;

public class CommonHandler {

  private void doCoolStuffBefore() { /* do some cool stuff */ }
  private void doCoolStuffAfter() { /* do some cool stuff */ }

  public  R getResult(Function0 provider) {
    R result;
    doCoolStuffBefore();
    try {
      result = provider.invoke();
    } finally {
      doCoolStuffAfter();
    }
    return result;
  }

}

NullableHandler.kt

如果操作引发异常,则返回null的处理程序版本。结果的类型为R?

class NullableHandler : CommonHandler() {
  override fun  getResult(provider: Function0): R? {
    return try {
      super.getResult(provider)
    } catch(ex: Throwable) {
      null
    }
  }
}

ThrowingHandler.kt

处理程序版本,将错误包装在其内部异常类型中。结果类型为R

class ThrowingHandler : CommonHandler() {

  class WrappedException(message: String, cause: Throwable?): Exception(message, cause)

  override fun  getResult(provider: Function0): R {
    return try {
      super.getResult(provider)
    } catch(ex: Throwable) {
      throw WrappedException("Throwing handler failed with exception: ${ex.javaClass.name}", ex)
    }
  }

}

Api.kt

基本上我们不拥有并且不能修改的任何API。

object Api {

  fun find(query: String): Int =
      if (query.length > 3) 42
      else throw NoSuchElementException("Not found for $query")

  fun select(query: String): String =
      if (query.count { it == 'x' } > 2) "Selected"
      else throw NoSuchElementException("Not found for $query")

}

现在,有了以上所有类,我们可以实现API包装器:

object ApiProxy {

  private val throwingHandler = ThrowingHandler()
  private val nullableHandler = NullableHandler()

  fun find(query: String): Int = throwingHandler.getResult { Api.find(query) }
  fun findOrNull(query: String): Int? = nullableHandler.getResult { Api.find(query) }

  fun select(query: String): String = throwingHandler.getResult { Api.select(query) }
  fun selectOrNull(query: String): String? = nullableHandler.getResult { Api.select(query) }

}

我的问题是,我如何实现类似的层次结构而又不依赖Java,这样就存在单个类/接口,其方法可以返回R类型或R?类型。据我所知,我们无法使用R!语法在Kotlin中显式声明平台类型。

思路一:


我认为简单的解决方案是使CommonHandler的getResult返回可为空的结果,然后处理null返回,因为这是一个例外。至少那是我一开始想到的。

所以,根据提示,我们将公共处理程序定义为

open class CommonHandler {
    private fun doCoolStuffBefore() { /* do some cool stuff */
    }

    private fun doCoolStuffAfter() { /* do some cool stuff */
    }

    open fun  getResult(provider: Function0): R? {
        doCoolStuffBefore()
        return try {
            provider.invoke()
        } finally {
            doCoolStuffAfter()
        }
    }
}

NullableHandler不变,但ThrowingHandler做了

class ThrowingHandler : CommonHandler() {

    class WrappedException(message: String, cause: Throwable?): Exception(message, cause)

    override fun  getResult(provider: Function0): R {
        return try {
            super.getResult(provider) ?: throw AnotherWrappedException("whooops")
        } catch(ex: Throwable) {
            throw WrappedException("Throwing handler failed with exception: ${ex.javaClass.name}", ex)
        }
    }

}

如果不为null,则使用elvis运算符返回该值;如果为null,则抛出AnotherWrappedException("whooops")

您怎么看?能为您工作吗?

思路二:


如果将返回类型定义为可为空的R:

open class CommonHandler {
    private fun doCoolStuffBefore() { /* do some cool stuff */
    }

    private fun doCoolStuffAfter() { /* do some cool stuff */
    }

    open fun  getResult(provider: () -> R): R? {
        doCoolStuffBefore()
        return try {
            provider.invoke()
        } finally {
            doCoolStuffAfter()
        }
    }

}

然后您可以将范围缩小到子类中的不可为N的R。您的ThrowableHandler假定API无法返回null,因此在此处使用!!运算符并将任何KotlinNPE包裹在WrappedException中是完全合理的。如果需要包装可以返回null的API,那么我认为您还需要NullableThrowableHandler。

class ThrowingHandler : CommonHandler() {

    class WrappedException(message: String, cause: Throwable?): Exception(message, cause)

    override fun  getResult(provider: () -> R): R {
        return try {
            super.getResult(provider)!!
        } catch(ex: Throwable) {
            throw WrappedException("Throwing handler failed with exception: ${ex.javaClass.name}", ex)
        }
    }
}

上一篇:有什么方法可以使这种典型方法通用?

下一篇:如何检测Linux OS是否使用KDE或Gnome环境

您可能感兴趣的文章

相关阅读

热门软件源码

最新软件源码下载