【问题标题】:Does Kotlin `by` keyword work for nullable delegate?Kotlin `by` 关键字是否适用于可为空的委托?
【发布时间】:2019-08-16 22:06:16
【问题描述】:

我对 Kotlin 编译器功能特别是 by 感到非常兴奋 - 它节省了生成凝胶代码的时间:

https://kotlinlang.org/docs/reference/delegation.html

但我希望委托是可空并委托代码首先检查它是否为空,如果是则返回:

interface Base {
    val message: String
    fun print()
}

class BaseImpl(val x: Int?) : Base {
    override val message = "BaseImpl: x = $x"
    override fun print() { println(message) }
}

class Derived(b: Base?) : Base by b {
    // This property is not accessed from b's implementation of `print`
    override val message = "Message of Derived"
}

fun main() {
    val b = BaseImpl(10)
    val derived = Derived(b)
    derived.print()
    println(derived.message)
}

编译时 ^ 我得到Type mismatch: inferred type is Base? but Base was expected。

Kotlin 仍然可以吗?

更详细地说,我希望 Kotlin 编译器生成对 https://developer.android.com/reference/android/webkit/WebChromeClient 中包装 impl (extWebChromeClient) 的转发调用,如下所示:

private WebChromeClient intWebChromeClient = new WebChromeClient()
  {
    @Override
    public void onReceivedTitle(WebView view, String title)
    {
      if (extWebChromeClient != null)
      {
        extWebChromeClient.onReceivedTitle(view, title);
      }
    }
 ...

【问题讨论】:

  • 无论这是否可行,我都觉得不合理。无论如何,它只适用于void 方法。
  • 运算符 ?. 通过使返回类型为空来处理非 void 方法。这不可能在这里工作,因为它会违反Base 的合同。理论上,没有什么可以阻止它在所有方法都返回可空类型的接口上工作。
  • @Marko 我觉得这很合理。覆盖可能需要返回值的方法。无论如何,它节省了大量编写样板代码的时间。
  • 无论如何你都不能委托WebChromeClient,因为它是一个类,而不是一个接口。
  • 实际上,由于WebChromeClient 不是抽象的,并且它的回调默认情况下什么都不做,为什么不首先让extWebChromeClient 不可为空并将其初始化/设置为WebChromeClient() 而不是null?无需生成任何代码。

标签: android kotlin delegation


【解决方案1】:

您可以使用dynamic proxies 自己制作,但我并不真正推荐它。请注意,对于非void 方法,没有办法要求覆盖它们。下面的实现只是无条件地为它们抛出异常,但您仍然可以将它们称为非空x。

inline fun <reified T : Any> nullableProxy(x: T?): T {
    val handler = InvocationHandler { _, method, args ->
        if (method.returnType == Void.TYPE) {
            if (x != null) {
                method.invoke(x, *(args ?: arrayOf()))
            }
        } else 
            throw UnsupportedOperationException("Non-void method")
    }

    return Proxy.newProxyInstance(
        T::class.java.classLoader,
        arrayOf(T::class.java),
        handler) as T
}

class Derived(b: Base?) : Base by nullableProxy(b)

这也不会像直接实现方法那样执行。

【讨论】:

  • 这会让它太慢恕我直言
  • @4ntoine 更慢?是的,如答案中所述。是否太慢取决于具体情况。另一种方法是代码生成。以防万一:当您说编写样板代码可以节省大量时间时,您确实意识到每个Base 接口只编写一次代码,而不是每个Derived 类编写一次代码?
  • 我的意思是反射调用比直接调用慢得多(由 kotlin 编译器生成的代码)所以主要的性能下降是因为这个而不是因为“特定情况”。我在 JSON-RPC 库中使用了动态代理,并且测量了性能下降。此外,您的代码可以抛出 InvocationTargetException 而不是原始异常,这是有道理的:应该重新抛出原始异常原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 2020-07-04
  • 1970-01-01
  • 1970-01-01
  • 2018-08-16
  • 1970-01-01
相关资源
最近更新 更多