Kotlin泛型上界与扩展函数

泛型场景

先由我们熟悉的Java说起,有时在使用泛型的时候,会有这样的场景。

比如我们封装一个参数类,里面提供一个放入参数的方法,并且方法返回类型为它自身,以便调用时进行链式调用。

泛型递归和链式调用

为了让它的子类在调用时也返回它自身,我们定义一个泛型继承于它,并且返回值为这个泛型。如下:

然后我们的子类,如下声明参数:

调用的时候就可以实现链式调用了:

这是第一种场景。

子类的泛型递归

在上面的前提下,比如我们发现有许多参数类都要声明一个共同的参数,于是我们继承自再封装一个类,并且还是使用递归泛型,使得子类调用这个类声明的方法的时候能够返回子类的类型,以便一路链式调用。如下:

这是第二种场景。

直接使用父类

在项目里,还有另一种场景:某个接口声明里,我们只需要类,不需要添加其他参数,所以不需要再去声明一个它的子类。

比如我们使用Retrofit时,声明如下的接口:

这是一个注销退出的接口,需要传token,但是我们已经在实现了的类里实现了token的添加,这里我们只需要一个参数。

这是第三种场景。

这几种场景,使用Java如此实现是没有问题的,但是转成Kotlin代码问题却出来了。

Kotlin的问题与方式

使用时必须指定类型的泛型的问题

我们使用Idea的转成Kotlin代码的功能,将类转成Kotlin代码,如下:

这是编译不过去的,因为Kotlin的泛型不能省略,所以原来Java代码里泛型里面的就被转成了,但已经约束了是有上界的,所以不能使用,我们改为。这样看起来第一种场景是通过了。

但是我们刚才的报错了,因为我们声明的是,但它需要继承自。没关系,如上转为Kotlin代码就好了:

第二种场景也没问题。

现在第三种场景的问题来了。

我们调用需要传一个参数,把调用代码转为Kotlin之后报错了,因为Params没有指定泛型,推断出来的是,它想要的是。但是我们把它改为,里面的又报了这一个问题。好吧,这对Kotlin而言,是个无穷无尽的泛型递归问题。

这里的问题就在于,接口是用Java声明的,没有指定泛型参数的类型,所以Kotlin这边推断出来的是,而由于的泛型有其上界,所以Kotlin创建不了这个类型的实例。

我们把接口的代码也转成kotlin,会发现接口的参数类型只能声明为才能编译通过。但是会有两个问题:

一是使用的时候,上面的问题还是没有解决。所以只能传的其他子类,比如传个前面定义的。暂时忽略这个不可理喻的调用,至少编译通过了。

二是运行的时候Retrofit报错了,因为它不允许参数的泛型类型为通配符。就相当于Java中的。

所以Kotlin使用泛型来解决这些问题是走不通的。

Kotlin的方式

我们回顾一下,其实只是想要三个诉求:

类封装一个的方法,方法能够返回它的子类。

的子类能够封装一些方法,方法能返回它的子类的子类。

能够被直接调用及创建它的实例。

Kotlin做不到吗?不是的。如果是递归泛型是Java相对于Kotlin的特性,那Kotlin也有相对于Java的语言特性可以实现以上的需求,那就是扩展函数。

现在我们使用扩展函数来实现前面的功能。

首先,我们的Params也不需要声明泛型了,它定义如下:

由于没有泛型参数,所以第三个种场景也就解决了。然后我们在类里声明一个扩展函数,在这里我们使用了泛型。我们让这个泛型有上界,返回值类型是这个泛型。这样第一种场景也解决了。

对于第二种场景,也是使用扩展函数来实现,但是这里的扩展函数需要声明在类的外面,否则无法调用到这个函数。

这样问题就解决了。

对比Java,我们也可以看到,Kotlin的实现看起来更简洁清晰。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181212G0AAA300?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券