【问题标题】:Kotlin bound type parameters extension overloadKotlin 绑定类型参数扩展重载
【发布时间】:2018-07-03 13:27:23
【问题描述】:

是否可以根据绑定的类型参数重载扩展函数?例如:

abstract class BaseActivity : AppCompatActivity() {

    fun <T : Drawable> Int.get(): T? 
        = ContextCompat.getDrawable(this@BaseActivity, this@get) as T?
    fun <T : View> Int.get(): T 
        = findViewById(this)

}

示例用法:

R.id.webView.get<WebView>() // finds webView
R.drawable.image.get<Drawable>() // gets drawable from resources

(Android 代码与问题无关,这正是我所拥有的)

但它给了我错误:

Conflicting overloads: public fun <T : Drawable> Int.get(): T? defined in xxx.BaseActivity, 
public final fun <T : View> Int.get(): T defined in xxx.BaseActivity

不应该吗?

【问题讨论】:

    标签: generics kotlin overloading


    【解决方案1】:

    删除泛型后,这两个函数将编译为相同的函数签名。

    例如,它可能是这样的:

    @NotNull
    public final String get() {
      ...
    }
    

    编译器无法知道调用哪个函数。

    对此的解决方案可以是具体化类型参数。

        interface Some
        interface Other
    
        inline fun <reified T> get() = when (T::class) {
            Some::class -> "hey"
            Other::class -> null
            else -> throw UnsupportedOperationException()
        }
    

    但是,您必须取消类型限制。

    【讨论】:

    • "删除泛型后,这两个函数将编译为相同的函数签名。"不,他们没有:返回类型不同。
    • @AlexeyRomanov 返回类型不是签名的一部分,参见here 例如: 定义:方法声明的两个组成部分包括方法签名——方法的名称和参数类型。
    • Java 语言确实如此,但 JVM 确实允许基于返回类型的重载(尽管经过检查,相关术语是“描述符”和“签名”,其中也包括泛型)。
    【解决方案2】:

    使用@JvmName 解决平台声明冲突的“正常”方法在这里不起作用:Kotlin 允许通过值参数的类型进行重载,而不是通过类型参数的边界。

    这可以(丑陋地)通过添加无用的值参数和(同样无用的)默认值来解决。

    // unchanged
    fun <T : Drawable> Int.get(): T? =
            ContextCompat.getDrawable(this@BaseActivity, this@get) as T?
    
    // added a parameter
    fun <T : View> Int.get(dummy: Nothing? = null): T =
            findViewById(this)
    

    一些不相关的注释:

    • 这么小的方法应该是inline,尤其是参数默认值的方法
    • findViewById 可以替换为 Kotlin Android extensions
    • Anko 库有许多有用的扩展和快捷方式,包括用于获取资源甚至从没有 XML 的代码构建布局的扩展和快捷方式

    【讨论】:

    • 不错。关于 Kotlin Android 扩展,it's no longer advocated for usage
    • 我不听从 Google 的蹩脚建议。他们一天可以发明和弃用 256 次东西,而我只是想解决我的问题。而且无论 Kotlin Android 扩展多么出色,XML 布局和可绘制对象都应该被烧毁。
    【解决方案3】:

    是否可以根据绑定的类型参数重载扩展函数?

    一般来说,是的。问题是参数只出现在返回类型中,Java 的规则(Kotlin 在这方面遵循)不允许基于返回类型重载方法,因为返回类型不用于重载解析。这个决定是在泛型出现在 Java 中之前做出的,因为那时编译器永远无法选择更具体的方法。后来本来可以修改的,但没有。

    如果你有一个T 参数,它会起作用。

    【讨论】:

      猜你喜欢
      • 2018-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多