0

考虑这个例子:


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> R getResult(Function0<R> provider) {
    R result;
    doCoolStuffBefore();
    try {
      result = provider.invoke();
    } finally {
      doCoolStuffAfter();
    }
    return result;
  }

}

NullableHandler.kt

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

class NullableHandler : CommonHandler() {
  override fun <R> getResult(provider: Function0<R>): 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 <R> getResult(provider: Function0<R>): 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 中显式声明平台类型。

4

2 回答 2

1

我认为简单的解决方案是让 CommonHandler 中的 getResult 返回可为空的结果,然后处理 null 返回,因为它是一个异常。至少我一开始是这么想的。

因此,按照提示,我们将通用处理程序定义为

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

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

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

NullableHandler 不会改变,但 ThrowingHandler 会改变

class ThrowingHandler : CommonHandler() {

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

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

}

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

你怎么看?它对你有用吗?

于 2020-02-25T14:24:58.437 回答
1

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

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

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

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

}

然后,您可以将范围缩小到子类中的不可为空的 R。

class ThrowingHandler : CommonHandler() {

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

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

您的 ThrowableHandler 假设 API 无法返回 null,因此在!!此处使用运算符并让任何 KotlinNPE 包装在 WrappedException 中是完全合理的。这实际上比您已经拥有的要好,因为意外异常包含在您的包装器中,而不是冒泡到代码中其他地方的 Java NPE。如果您需要包装一个应该能够返回 null 的 API,那么我认为您还需要一个 NullableThrowingHandler。

于 2020-02-25T14:52:59.893 回答