[译]Kotlin泛型中什么时候该用类型形参约束?

翻译说明:java

原标题: When (and when not) to Use Type Parameter Constraints in Kotlinapp

原文地址: typealias.com/guides/when…dom

原文做者: Dave Leedside

以前的Kotlin文章,欢迎查看:

翻译系列:函数

原创系列:post

实战系列:ui

简述(又来皮一下):

今天这篇文章依旧很简单,只要搞懂一个东西就能够了。那就是泛型中的类型形参的约束,这个概念在Java中也有的。可是咱们有个疑惑是什么状况下使用泛型类型形参呢?spa

进入正题(开始表演...)

当你在声明一个泛型时,Kotlin容许你给这个泛型的类型形参增长约束条件,换言之就是把类型形参可接受的类型实参限制在一个类型范围内。那这个有什么做用呢?让咱们一块儿来看下面这个例子:插件

想像下有这么个需求场景: 假设你家里有几只宠物,你想选择一个最喜欢的:翻译

fun <T> chooseFavorite(pets: List<T>): T {
    val favorite = pets[random.nextInt(pets.size)]
    // This next line won't compile - because `name` can't be resolved
    println("My favorite pet is ${favorite.name}")
    return favorite
}
复制代码

println()函数声明不会经过编译,由于你没法经过T来引用name属性。由于T能够是调用者指定的任何内容。例如,像如下代码实现,T就是一个Int类型,很明显它就没有name属性:

chooseFavorite(listOf(1, 2, 3))
复制代码

解决方法一 - 放弃使用泛型

你能够尝试改造一下这个函数,把泛型去掉:

fun chooseFavorite(pets: List<Pet>): Pet {
    val favorite = pets[random.nextInt(pets.size)]
    println("My favorite pet is ${favorite.name}")
    return favorite
}
复制代码

这个处理看起来貌似没有什么问题,可是咱们会遇到一种不想要的返回值类型。如下是咱们调用该函数时会发生的状况:

val pets: List<Dog> = listOf(Dog("Rover"), Dog("Sheba"))
val favorite: Pet = chooseFavorite(pets)
复制代码

尽管咱们声明定义的是List< Dog >,可是经过chooseFavorite返回的是一个Pet类型,除非这里咱们使用强制类型转换。

解决办法二 - 使用类型形参约束

咱们能够经过指定上限来限制类型形参-换句话说就是指定你想要接收的超类型。在咱们的例子中,咱们但愿此函数做为List <Pet>处理List <Dog>List <Cat>也就是既包含Cat类型也包含Dog类型的混合Pet类型的列表中。

fun <T : Pet> chooseFavorite(pets: List<T>): T {
    val favorite = pets[random.nextInt(pets.size)]
    println("My favorite pet is ${favorite.name}")
    return favorite
}
复制代码

这里的类型形参的声明是<T:Pet>,这个Pet就是上界约束。如今咱们已经指定了这个,调用代码只能传递Pet类型以及它的子类型。

val pets: List<Dog> = listOf(Dog("Rover"), Dog("Sheba"))
val favorite: Dog = chooseFavorite(pets)
复制代码

使用建议

下面两种是你须要使用类型形参约束状况:

  1. 当你在某个类型上调用特定的函数或属性(即某个类型的类独有的函数和属性)
  2. 当你但愿在函数返回时保留某个特定类型

这是一个快速的“备忘单”表,可帮助您决定哪一种状况使用什么?

须要调用成员(类的成员函数或属性) 不须要调用成员(类的成员函数或属性)
须要保留类型 使用带有类型参数约束的泛型 使用不带类型参数约束的泛型
不须要保留类型 使用非泛型和适当的抽象 使用Java中的原生态类型

如何指定约束

约束还有不少 - 您能够指定多个参数的约束,以及对同一参数的多个约束。对于全部细节,请查看概念文章Type Parameter Constraint。您还能够在Kotlin的官方参考文档中快速查看经常使用约束。

读者有话说

本篇文章核心点在于什么状况下该使用泛型约束,做者总结的很好就是那种表格,理解和掌握了那张表格,那么你在使用泛型形参约束上就会成竹在胸。关于那个表格可能有点难理解,我这里再补充解释一下:

  • 首先,解释下是否须要调用成员,意思是在定义函数声明内部是否使用了泛型形参约束上界类型中对应类的特定成员包括函数或属性,好比说chooseFavorite方法中的name属性就是Pet这个类特定的成员属性。总之一句话: 在函数定义内部是否须要调用特定类型的类的成员或属性,这也就直接决定了是否须要带类型参数约束,若是不须要调用成员,那么就不须要带类型参数约束
  • 而后,解释下是否须要保留类型,就是在定义一个函数的时候,返回值的类型是否要保留泛型形参约束上界类型,主要做用就是避免强制类型转换,例如例子中解决办法一就是去除泛型,而是用了抽象的父类Pet,即便传入的是List<Dog>,可是chooseFavorite方法返回依然是父类Pet,外部接收类型Dog因此避免不了强制类型转换。总之一句话: 是否须要保留类型,也就直接决定了是否使用泛型,若是不保留类型的话能够不使用泛型,函数外部接收者可使用抽象的父类来接收;若是保留类型,函数外部接收者就必须是明确一个类型,那么此时若是还用抽象父类就避免不了类型转换,那么此时就应该使用泛型
  • 最后,得出简单的总结:

一、是否须要调用成员决定了在使用泛型前提下,是否使用泛型类型参数约束;不然直接可使用抽象

二、是否保留类型决定了是否使用泛型

三、既保留类型又调用成员,则就是使用泛型且带形参约束条件

四、不保留类型又调用成员,则就是不使用泛型和适当的抽象

五、保留类型不调用成员,则就是使用泛型不带形参约束条件

六、不保留类型不调用成员,则就是使用java中原生态类型,例如Java中的List类型

欢迎关注Kotlin开发者联盟,这里有最新Kotlin技术文章,每周会不按期翻译一篇Kotlin国外技术文章。若是你也喜欢Kotlin,欢迎加入咱们~~~

相关文章
相关标签/搜索