Kotlin泛型上界与扩展函数
文章目录
- 泛型场景
- 泛型递归和链式调用
- 子类的泛型递归
- 直接使用父类
- Kotlin的问题与方式
- 使用时必须指定类型的泛型的问题
- Kotlin的方式
泛型场景
先由我们熟悉的Java说起,有时在使用泛型的时候,会有这样的场景。
比如我们封装一个参数类,里面提供一个放入参数的方法,并且方法返回类型为它自身,以便调用时进行链式调用。
泛型递归和链式调用
为了让它的子类在调用时也返回它自身,我们定义一个泛型继承于它,并且返回值为这个泛型。如下:
public class Params<T extends Params> extends HashMap<String, Object> {protected final T putParams(String key, Object value) {put(key, value);return (T) this;}
}
然后我们的子类,如下声明参数:
public class OrderParams extends Params<OrderParams> {public OrderParams page(int page) {return putParams("page", page);}public OrderParams pageSize(int size) {return putParams("page_items", size);}// ...
}
调用的时候就可以实现链式调用了:
final OrderParams params = new OrderParams().page(page).pageSize(Constants.PAGE_SIZE)//...
这是第一种场景。
子类的泛型递归
在上面的前提下,比如我们发现有许多参数类都要声明一个共同的参数,于是我们继承自Params
再封装一个类,并且还是使用递归泛型,使得子类调用这个类声明的方法的时候能够返回子类的类型,以便一路链式调用。如下:
public class ParkParams<T extends ParkParams> extends Params<T> {public final T park(String parkCode) {return putParams("park_code", parkCode);}
}
这是第二种场景。
直接使用父类
在项目里,还有另一种场景:某个接口声明里,我们只需要Params
类,不需要添加其他参数,所以不需要再去声明一个它的子类。
比如我们使用Retrofit时,声明如下的接口:
@POST("/iop/auth/logout")@NeedTokenObservable<DataResponse> logout(@Body Params params);
这是一个注销退出的接口,需要传token,但是我们已经在实现了Converter<T, RequestBody>
的类里实现了token的添加,这里我们只需要一个Params
参数。
这是第三种场景。
这几种场景,使用Java如此实现是没有问题的,但是转成Kotlin代码问题却出来了。
Kotlin的问题与方式
使用时必须指定类型的泛型的问题
我们使用Idea的转成Kotlin代码的功能,将Params
类转成Kotlin代码,如下:
open class Params<T : Params<*>> : HashMap<String, Any?>() {protected fun putParams(key: String, value: Any?): T {put(key, value)return this as T}
}
这是编译不过去的,因为Kotlin的泛型不能省略,所以原来Java代码里泛型里面的<T extends Params>
就被转成了<T : Params<*>>
,但Params
已经约束了是有上界的,所以不能使用*
,我们改为class Params<T : Params<T>>
。这样看起来第一种场景是通过了。
但是我们刚才的ParkParams
报错了,因为我们声明的是public class ParkParams<T extends ParkParams> extends Params<T>
,但它需要T
继承自Params
。没关系,如上转为Kotlin代码就好了:
open class ParkParams<T : ParkParams<T>> : Params<T>() {fun park(parkCode: String): T {return putParams("park_code", parkCode)}
}
第二种场景也没问题。
现在第三种场景的问题来了。
我们调用logout(params)
需要传一个Params
参数,把调用代码转为Kotlin之后报错了,因为Params没有指定泛型,推断出来的是Params<*>
,它想要的是Params<Params<*>>
。但是我们把它改为Params<Params<*>>
,里面的<params<*>>
又报了这一个问题。好吧,这对Kotlin而言,是个无穷无尽的泛型递归问题。
这里的问题就在于,logout(params)
接口是用Java声明的,没有指定泛型参数的类型,所以Kotlin这边推断出来的是Params<*>
,而由于Params
的泛型有其上界,所以Kotlin创建不了这个类型的实例。
我们把logout(params)
接口的代码也转成kotlin,会发现接口的参数类型只能声明为Params<*>
才能编译通过。但是会有两个问题:
一是使用的时候,上面的问题还是没有解决。所以只能传Params
的其他子类,比如传个前面定义的OrderParams
。暂时忽略这个不可理喻的调用,至少编译通过了。
二是运行的时候Retrofit报错了,因为它不允许参数的泛型类型为通配符。<*>
就相当于Java中的<?>
。
所以Kotlin使用泛型来解决这些问题是走不通的。
Kotlin的方式
我们回顾一下,其实只是想要三个诉求:
Params
类封装一个putParams(key, value)
的方法,方法能够返回它的子类。Params
的子类能够封装一些方法,方法能返回它的子类的子类。Params
能够被直接调用及创建它的实例。
Kotlin做不到吗?不是的。如果是递归泛型是Java相对于Kotlin的特性,那Kotlin也有相对于Java的语言特性可以实现以上的需求,那就是扩展函数。
现在我们使用扩展函数来实现前面的功能。
首先,我们的Params也不需要声明泛型了,它定义如下:
open class Params : HashMap<String, Any?>() {fun <T : Params> T.putParams(key: String, value: Any?): T {put(key, value)return this}
}
由于Params
没有泛型参数,所以第三个种场景也就解决了。然后我们在Params
类里声明一个扩展函数,在这里我们使用了泛型。我们让这个泛型有Params
上界,返回值类型是这个泛型。这样第一种场景也解决了。
对于第二种场景,也是使用扩展函数来实现,但是这里的扩展函数需要声明在类的外面,否则无法调用到这个函数。
open class ParkParams : Params()fun <T : ParkParams> T.park(parkCode: String) = putParams("park_code", parkCode)
这样问题就解决了。
对比Java,我们也可以看到,Kotlin的实现看起来更简洁清晰。
Kotlin泛型上界与扩展函数相关推荐
- Kotlin学习:Kotlin泛型
Kotlin 泛型 泛型,即 "参数化类型",将类型参数化,可以用在类,接口,方法上. 与 Java 一样,Kotlin 也提供泛型,为类型安全提供保证,消除类型强转的烦恼. 声明 ...
- Kotlin学习笔记(五) 扩展函数 扩展属性
2019独角兽企业重金招聘Python工程师标准>>> 扩展函数和属性可以定义在对应的类中和kotlin文件中; 1.扩展函数 1.扩展函数可以由对象直接调用,在扩展函数中,用thi ...
- kotlin泛型_Kotlin泛型
kotlin泛型 In this tutorial, we'll be looking in Kotlin Generics and Variances. 在本教程中,我们将研究Kotlin的泛型和变 ...
- 一起来学Kotlin:概念:7. Kotlin 函数介绍:扩展函数,中缀函数,运算符函数,带有varrag输入的函数
Kotlin:概念:7. Kotlin 函数介绍:扩展函数,中缀函数,运算符函数,带有varrag输入的函数,内联函数 这里介绍Kotlin的不同种类函数,包括:一般的函数表达方式,扩展函数,中缀函数 ...
- Java泛型与Kotlin泛型
正文 本文主要列举Java泛型与Kotlin泛型的基本知识,以及两者的区别. 什么泛型 泛型程序设计是程序设计的一种风格或或规范.简单的说就是该类型可变,在编写代码时可以根据情况设置不同的类型.因为泛 ...
- kotlin 泛型约束
fun <T:Comparable<T>> sort(list :List<T>){} 冒号之后指定的类型就是泛型参数的上界,对于泛型参数T,只允许使用Compar ...
- 泛型---上界通配符和下界通配符
泛型 简介 java中的可变类型都是使用通配符(通常为大写字母)代替,例如public interface test<T>,其中的T就是通配符,只起到占位符的作用,可以是任意的大写字母 ...
- Kotlin 安利贴 2. 扩展函数
Hello,本次More Time 带来 Kotlin 超好用的特性--扩展函数 . 扩展函数的意思就是可以给一个类加上一个新函数. 有人说:那直接在类里面加呗. 有种东西叫做别人的代码,只能用,不能 ...
- Android Kotlin 文件读写操作扩展函数,Raw资源读取,Assets资源读取
1.保存内容到文件及文件读取 /*** 保存信息到File* @param fileName 文件名* @param content 保存内容* @param mode 模式*/ fun Contex ...
最新文章
- 三维重建 3D reconstruction 有哪些实用算法?
- bartlett方差齐性检验_基于R实现统计中的检验方法方差分析
- 多位诺贝尔奖获得者推荐的精品力作:《大数据时代的调查师》
- python抓取股票数据_Python自动获取当日所有股票数据
- C#一探究竟——枚举
- java_jdbc_基本连接池
- 使用HTML5技术控制电脑或手机上的摄像头(转载)
- java面试宝典第五版,《程序员面试宝典(第5版)》和《Java程序员面试宝典(第4版)》的一些看法......
- 什么是订单管理系统OMS
- 微信小程序中的转发功能
- 触摸屏 java_价值10W大洋的触摸屏技术揭秘。。。
- 共享计算机用户帐户限制怎么办,系统之家Win7系统无法共享提示用户账户限制怎么办...
- 苏州承泽医疗科技面经
- mysql主从同步错误:The slave I/O thread stops because master and slave have equal MySQL server UUIDs
- <医学断层图像三维重建面绘制算法的研究>——第一遍阅读
- select获取选中的option(包含value和text,重点是text怎么获取)
- ucharts折线图出现断点时画小圆圈(当线的width很大时,小圆圈会很大)
- 联发科射频工程师题目_MTK联发科技面试经验
- revit二次开发2016
- mysql_slow_详解MySQL中SlowLog的配置方法(图文)
热门文章
- 巴比特 | 元宇宙每日必读:企业如何将业务转移到元宇宙?这7大步骤了解一下...
- 基于SSM的医院预约挂号系统 JAVA MYSQL
- Android 10.0 SystemUI状态栏屏蔽掉通知栏不显示通知
- ldb文件matlab,MongoDB v4.2.2 安装与配置及常规操作
- 中国有机盐行业市场供需与战略研究报告
- 如何利用自己的数据制作社交地图?只显示可视区域内的标注
- 电能计量芯片HLW8110/HLW8112
- 大数据 (三) BI报表系统 superset 2 制作表格
- Winserver2016安装Exchange2016总结(一堆坑)
- 2018年最新从PayPal提现美金的方法(实战教程)!