[译]在Kotlin中何时该用类型形参约束?

2023-10-30 18:10

本文主要是介绍[译]在Kotlin中何时该用类型形参约束?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

翻译说明:

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

原文地址: https://typealias.com/guides/when-to-use-type-parameter-constraints/

原文作者: Dave Leeds

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

翻译系列:

  • [译] 一个简单方式教你记住Kotlin的形参和实参
  • [译]Kotlin中是应该定义函数还是定义属性?
  • [译]如何在你的Kotlin代码中移除所有的!!(非空断言)
  • [译]掌握Kotlin中的标准库函数: run、with、let、also和apply
  • [译]有关Kotlin类型别名(typealias)你需要知道的一切
  • [译]Kotlin中是应该使用序列(Sequences)还是集合(Lists)?
  • [译]Kotlin中的龟(List)兔(Sequence)赛跑
  • [译]Effective Kotlin系列之考虑使用静态工厂方法替代构造器
  • [译]Effective Kotlin系列之遇到多个构造器参数要考虑使用构建器

原创系列:

  • 有关Kotlin属性代理你需要知道的一切
  • 浅谈Kotlin中的Sequences源码解析
  • 浅谈Kotlin中集合和函数式API完全解析-上篇
  • 浅谈Kotlin语法篇之lambda编译成字节码过程完全解析
  • 浅谈Kotlin语法篇之Lambda表达式完全解析
  • 浅谈Kotlin语法篇之扩展函数
  • 浅谈Kotlin语法篇之顶层函数、中缀调用、解构声明
  • 浅谈Kotlin语法篇之如何让函数更好地调用
  • 浅谈Kotlin语法篇之变量和常量
  • 浅谈Kotlin语法篇之基础语法

实战系列:

  • 用Kotlin撸一个图片压缩插件ImageSlimming-导学篇(一)
  • 用Kotlin撸一个图片压缩插件-插件基础篇(二)
  • 用Kotlin撸一个图片压缩插件-实战篇(三)
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用

简述(又来皮一下):

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

进入正题(开始表演…)

当你在声明一个泛型时,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 resolvedprintln("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所以避免不了强制类型转换。总之一句话: 是否需要保留类型,也就直接决定了是否使用泛型,如果不保留类型的话可以不使用泛型,函数外部接收者可以使用抽象的父类来接收;如果保留类型,函数外部接收者就必须是明确一个类型,那么此时如果还用抽象父类就避免不了类型转换,那么此时就应该使用泛型
  • 最后,得出简单的总结:

1、是否需要调用成员决定了在使用泛型前提下,是否使用泛型类型参数约束;否则直接可以使用抽象

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

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

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

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

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

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

Kotlin系列文章,欢迎查看:

Kotlin邂逅设计模式系列:

  • 当Kotlin完美邂逅设计模式之单例模式(一)

数据结构与算法系列:

  • 每周一算法之二分查找(Kotlin描述)

翻译系列:

  • [译] Kotlin中关于Companion Object的那些事
  • [译]记一次Kotlin官方文档翻译的PR(内联类)
  • [译]Kotlin中内联类的自动装箱和高性能探索(二)
  • [译]Kotlin中内联类(inline class)完全解析(一)
  • [译]Kotlin的独门秘籍Reified实化类型参数(上篇)
  • [译]Kotlin泛型中何时该用类型形参约束?
  • [译] 一个简单方式教你记住Kotlin的形参和实参
  • [译]Kotlin中是应该定义函数还是定义属性?
  • [译]如何在你的Kotlin代码中移除所有的!!(非空断言)
  • [译]掌握Kotlin中的标准库函数: run、with、let、also和apply
  • [译]有关Kotlin类型别名(typealias)你需要知道的一切
  • [译]Kotlin中是应该使用序列(Sequences)还是集合(Lists)?
  • [译]Kotlin中的龟(List)兔(Sequence)赛跑

原创系列:

  • 教你如何完全解析Kotlin中的类型系统
  • 如何让你的回调更具Kotlin风味
  • Jetbrains开发者日见闻(三)之Kotlin1.3新特性(inline class篇)
  • JetBrains开发者日见闻(二)之Kotlin1.3的新特性(Contract契约与协程篇)
  • JetBrains开发者日见闻(一)之Kotlin/Native 尝鲜篇
  • 教你如何攻克Kotlin中泛型型变的难点(实践篇)
  • 教你如何攻克Kotlin中泛型型变的难点(下篇)
  • 教你如何攻克Kotlin中泛型型变的难点(上篇)
  • Kotlin的独门秘籍Reified实化类型参数(下篇)
  • 有关Kotlin属性代理你需要知道的一切
  • 浅谈Kotlin中的Sequences源码解析
  • 浅谈Kotlin中集合和函数式API完全解析-上篇
  • 浅谈Kotlin语法篇之lambda编译成字节码过程完全解析
  • 浅谈Kotlin语法篇之Lambda表达式完全解析
  • 浅谈Kotlin语法篇之扩展函数
  • 浅谈Kotlin语法篇之顶层函数、中缀调用、解构声明
  • 浅谈Kotlin语法篇之如何让函数更好地调用
  • 浅谈Kotlin语法篇之变量和常量
  • 浅谈Kotlin语法篇之基础语法

Effective Kotlin翻译系列

  • [译]Effective Kotlin系列之考虑使用原始类型的数组优化性能(五)
  • [译]Effective Kotlin系列之使用Sequence来优化集合的操作(四)
  • [译]Effective Kotlin系列之探索高阶函数中inline修饰符(三)
  • [译]Effective Kotlin系列之遇到多个构造器参数要考虑使用构建器(二)
  • [译]Effective Kotlin系列之考虑使用静态工厂方法替代构造器(一)

实战系列:

  • 用Kotlin撸一个图片压缩插件ImageSlimming-导学篇(一)
  • 用Kotlin撸一个图片压缩插件-插件基础篇(二)
  • 用Kotlin撸一个图片压缩插件-实战篇(三)
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用

这篇关于[译]在Kotlin中何时该用类型形参约束?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/309682

相关文章

SQL中的外键约束

外键约束用于表示两张表中的指标连接关系。外键约束的作用主要有以下三点: 1.确保子表中的某个字段(外键)只能引用父表中的有效记录2.主表中的列被删除时,子表中的关联列也会被删除3.主表中的列更新时,子表中的关联元素也会被更新 子表中的元素指向主表 以下是一个外键约束的实例展示

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

poj 3159 (spfa差分约束最短路) poj 1201

poj 3159: 题意: 每次给出b比a多不多于c个糖果,求n最多比1多多少个糖果。 解析: 差分约束。 这个博客讲差分约束讲的比较好: http://www.cnblogs.com/void/archive/2011/08/26/2153928.html 套个spfa。 代码: #include <iostream>#include <cstdio>#i

poj 3169 spfa 差分约束

题意: 给n只牛,这些牛有些关系。 ml个关系:fr 与 to 牛间的距离要小于等于 cost。 md个关系:fr 与 to 牛间的距离要大于等于 cost。 隐含关系: d[ i ] <= d[ i + 1 ] 解析: 用以上关系建图,求1-n间最短路即可。 新学了一种建图的方法。。。。。。 代码: #include <iostream>#include

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

POJ 1364差分约束

给出n个变量,m个约束公式 Sa + Sa+1 + .... + Sa+b < ki or > ki ,叫你判断是否存在着解满足这m组约束公式。 Sa + Sa+1   +   .+ Sa+b =  Sum[a+b] - Sum[a-1]  . 注意加入源点n+1 。 public class Main {public static void main(Strin

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

Mysql BLOB类型介绍

BLOB类型的字段用于存储二进制数据 在MySQL中,BLOB类型,包括:TinyBlob、Blob、MediumBlob、LongBlob,这几个类型之间的唯一区别是在存储的大小不同。 TinyBlob 最大 255 Blob 最大 65K MediumBlob 最大 16M LongBlob 最大 4G