原文地址: Kotlin学习快速入门(7)——扩展的妙用 - Stars-One的杂货小窝

之前也模模糊糊地在用这个功能,也是十分方便,可以不用继承,快速给某个类增加新的方法,本篇便是来讲解下Kotlin中扩展这一概念的使用

说明

先解释一下,扩展的说明,官方文档上解释:

Kotlin 能够扩展一个类的新功能,而无需继承该类或者使用像装饰者这样的设计模式

简单来说,就是可以不用继承来让一个类多出一个方法或属性(成员变量),可能这样说比较抽象,我们以一个简单的例子来说

比如说,我们需要用到以下功能:

判断String对象是否其是否为null或未空白字符串,如果为null或空白字符串,则返回true,否则返回false

此功能挺好实现,但我们想要实现此功能,无非就是3种方法:

  1. 写个工具类StringUtil,然后传递有个String对象进去,方法返回
  2. 写个新的类,让其继承于String类,之后再新增方法
  3. 用装饰者模式,扩展类(这里不多解释装饰者模式,可以自己百度查阅下资料)

但上面的方法,估计第一种各位都明白,也是十分简单,但使用起来还是比较麻烦,还得将对象作为入参传递,如果使用Kotlin的扩展特性,还能变得更加简单

而剩下两种,改动均是较大,一般得看情况使用,也是不太推荐

扩展方法

我们以刚才上述说的功能为例,实现判断String对象是否其是否为null或未空白字符串,如果为null或空白字符串,则返回true,否则返回false此功能

语法及使用

首先,显示讲解下语法

fun 类名.方法名(参数列表...):返回值{

}

看起来稍微有些抽象,我们直接上示例:

fun String.isBlankOrNullString(): Boolean {
return this == null || this.trim().length == 0
}

需要注意的是,方法里的this就是当前调用此方法的String对象

扩展方法使用:

fun main(args: Array<String>) {
val str = ""
println(str.isBlankOrNullString())
}

PS: 这里的扩展方法写在了顶层,是全局可用的

注意点

  1. 扩展方法会区分作用域(全局和局部)
  2. 类中存在于扩展方法同名同参数列表,相当于重载,此时以扩展方法为主
  3. 扩展方法可接收可空类型

扩展方法作用域

扩展方法的声明位置,会决定此扩展方法的作用域

如下面示例:

fun main() {
val str = ""
println(str.isBlankOrNullString())
} class User {
val str = ""
} fun String.isBlankOrNullString(): Boolean {
return this == null || this.trim().length == 0
}

我们将方法写在了最外层(即与class关键字同级),此时,我们可以在任意的类中调用此方法

但如果我们稍微改一下,如下:

fun main() {
val str = ""
//这里会报错!!
//println(str.isBlankOrNullString())
} class User {
val str = "" fun sayHello() {
//类中可以正常使用
str.isBlankOrNullString()
} fun String.isBlankOrNullString(): Boolean {
return this == null || this.trim().length == 0
} }

扩展方法重载问题

由于是声明方法,可能会出现方法名重名的情况,即我们Java基础中说到的重载关系

这里,如果出现了重载的情况(方法名和参数列表相同),会以类中的方法为主(即会忽略掉扩展方法)

上面此句,是根据文档上总结得来的,实际上也是测试通过

fun main() {
Example().printFunctionType()
} class Example {
fun printFunctionType() { println("Class method") }
} fun Example.printFunctionType() { println("Extension function") }

最后输出的是Class method

但这里有个奇怪的情况,我以String的扩展为例,测试发现与上述结论不一致!!

以下是我的测试代码:

fun main() {
val str = ""
println(str.isNullOrBlank())
} fun String.isNullOrBlank(): Boolean {
println("进入我们的方法里")
return this == null || this.trim().length == 0
}

最终输出:

进入我们的方法里
true

看着打印,这明显就是进到我们定义的扩展方法里啊

研究了一番,发现原本的那个isNullOrBlank(),并不是String类中含有的方法

官方也是通过扩展方法来实现追加的,且是扩展的类是CharSequence,而且此类是个接口类,所有实现了此接口的类都有了isNullOrBlank()方法

而我们自己也是定义了个扩展方法,与官方的扩展方法发生了重载,于是我们的扩展方法便是把官方的扩展方法覆盖了

所以得出以下结论:

当类中存在某个方法,扩展方法与此方法发生重载关系,会以类中方法为主

某类已存在某个扩展方法,用户自定义扩展方法与该扩展方法发生重载,会以用户自定义扩展方法为主

当然,上面这是自己研究定下的,若是不太准确,希望各位可以指正!

扩展方法接收可空类型

上面,我们是定义的String类型扩展,当然,也可以给String?可空类型进行扩展方法

这样写也是没有问题的:

fun String?.isNullOrBlank(): Boolean {
println("进入我们的方法里")
return this == null || this.trim().length == 0
}

像这样,我们还可以直接给所有类型(Any类型)增加个toString()方法的扩展方法:

fun Any?.toString(): String {
if (this == null) return "null"
// 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
// 解析为 Any 类的成员函数
return toString()
}

但是,这个扩展方法是不起作用的!!

为什么呢?因为Any对象已经存在了toString()此方法,根据上面的结论,会以类中的方法优先!

扩展属性

除了方法,我们也可以实现扩展属性

语法

val 类型.属性名: 属性类型名
get() =

如有个示例,判断文件是否为md文件:

val File.isMdFile: Boolean
get() = extension.toLowerCase()=="md"

使用:

fun main() {
val file =File("D:\\tt.md")
println(file.isMdFile)
} val File.isMdFile: Boolean
get() = extension.toLowerCase()=="md"

相关作用域与上述扩展方法讲解的是一致的,这里不再赘述

扩展伴生对象

这里,感觉就是类似工具类的扩展吧,如果使用伴生对象,之后就可以类名.方法名去调用方法(类似Java中的静态方法)

如果我们想追加一些方法,也可以使用扩展来实现,如下例子

class MyClass {
companion object { } // 将被称为 "Companion"
} fun MyClass.Companion.printCompanion() { println("companion") } fun main() {
MyClass.printCompanion()
}

原理补充

Kotlin中的扩展函数,其实最后编译成class文件都会转为一个静态方法

这一过程实际上是由Kotlin编译器替我们实现了,我们只管吃语法糖就完事了!

我们以下面方法为例:

fun String?.isNullOrBlank(): Boolean {
println("进入我们的方法里")
return this == null || this.trim().length == 0
}

最终生成的静态方法:

// 这个类名就是顶层文件名+“Kt”后缀
public final class ExtendsionDemoKt {
// 扩展函数 isNullOrBlank 对应实际上是 Java 中的静态函数,并且传入一个接收者类型对象作为参数
public static final boolean isNullOrBlank(@NotNull CharSequence $this$isNullOrBlank) {
Intrinsics.checkParameterIsNotNull($this$isNullOrBlank, "$this$isNullOrBlank");
String var1 = "进入我们的方法";
boolean var2 = false;
System.out.println(var1);
return StringsKt.trim($this$isNullOrBlank).length() == 0;
}
}

如果我们isNullOrBlank还有参数的话,静态方法中除了CharSequence这个参数,还会多出其他参数

PS: 可以点开对应的class文件,然后使用tool->kotlin->Decompile Kotlin To Java,将class还原会java代码

参考

Kotlin学习快速入门(7)——扩展的妙用的更多相关文章

  1. Kotlin学习快速入门(4)——集合使用

    List,Set,Map都是集合 List 是一个有序集合,可通过索引(反映元素位置的整数)访问元素.元素可以在 list 中出现多次.列表的一个示例是一句话:有一组字.这些字的顺序很重要并且字可以重 ...

  2. Kotlin学习快速入门(1)——基本数据类型以及String常用方法使用

    本文适合有Java基础的人 Kotlin语法特点 相比java,省略括号,可以自动判断类型,省略new关键字,空指针捕获 主函数 kotlin文件(kt文件)中,只有要下列的方法,就可以运行,无需像之 ...

  3. Kotlin学习快速入门(2)——条件 数组 循环 方法

    条件 if条件判断 常用的判断和Java一样,这里提一下不同的用法 1.if可以作为三元运算符 val max = if (a > b) a else b 2.使用in判断是否在某个区间 val ...

  4. Kotlin学习快速入门(3)——类 继承 接口

    类 参考链接 类定义格式 使用class关键字定义,格式如下: class T{ //属性 //构造函数 //函数 //内部类 } Java Bean类 java bean类 //java bean类 ...

  5. Kotlin学习快速入门(5)——空安全

    介绍 kotlin中,对象可分为两种类型,可为空的对象和不可为空对象 默认为不可为空对象,代码检测如果发现不可为空对象赋予了null,则会标红报错. 可为空的对象,如果调用了方法,代码检测也会标红报错 ...

  6. pytest学习--快速入门

    一.pytest简介 Pytest是python的一种单元测试框架. pytest的特点: 入门简单,文档丰富 支持单元测试,功能测试 支持参数化,重复执行,部分执行,测试跳过 兼容其他测试框架(no ...

  7. pytorch入门--土堆深度学习快速入门教程

    工具函数 dir函数,让我们直到工具箱,以及工具箱中的分隔区有什么东西 help函数,让我们直到每个工具是如何使用的,工具的使用方法 示例:在pycharm的console环境,输入 import t ...

  8. php随笔3-thinkphp 学习-ThinkPHP3.1快速入门(1)基础

    ThinkPHP3.1快速入门(1)基础 简介 ThinkPHP是一个快速.简单的基于MVC和面向对象的轻量级PHP开发 框架,遵循Apache2开源协议发布,从诞生以来一直秉承简洁实用的设计原则,在 ...

  9. Wix学习整理(1)——快速入门HelloWorld

    原文:Wix学习整理(1)--快速入门HelloWorld 1 Wix简介 Wix是Windows Installer XML的简称,其通过类XML文件格式来指定了用于创建Windows Instal ...

随机推荐

  1. kubeadm 搭建 K8s

    kubeadm 搭建 K8s 本篇主要记录一下 使用 kubeadm 搭建 k8s 详细过程 ,环境使用 VirtualBox 构建的3台虚拟机 1.环境准备 操作系统:Centos7 (CentOS ...

  2. Jx.Cms开发笔记(五)-文章编辑页面标签设计

    标签页的样子 设计思路 与其他输入框一样,存在一个Label标签,由于这里不像其他输入框一样可以直接使用Row标签,所以这里需要额外增加. 使用Tag组件显示所有的标签,我们在Blazor 组件库 B ...

  3. 【基础】字符编码-ASCII、Unicode、utf-8

    一.各自背景 1. ASCII ASCII 只有127个字符,表示英文字母的大小写.数字和一些符号.但由于其他语言用ASCII编码表示字节不够,例如:常用中文需要两个字节,且不能和ASCII冲突,中国 ...

  4. 真实本人亲测Elasticsearch未授权访问漏洞——利用及修复【踩坑指南到脱坑!】

    如要转载请注明出处谢谢: https://www.cnblogs.com/vitalemontea/p/16105490.html 1.前言 某天"发现"了个漏洞,咳咳,原本以为这 ...

  5. 字节跳动构建Data Catalog数据目录系统的实践(上)

    作为数据目录产品,Data Catalog 通过汇总技术和业务元数据,解决大数据生产者组织梳理数据.数据消费者找数和理解数的业务场景,并服务于数据开发和数据治理的产品体系.本文介绍了字节跳动 Data ...

  6. 在C#中使用 SendMessage 实现操作外部其他程序上的控件教程

    一.C#代码实现 本案例使用的是c# winform .NET Framework 4.7.2 首先我们声明一个寻找窗体的函数 [DllImport("User32.dll", E ...

  7. 痞子衡嵌入式:大话双核i.MXRT1170之在线联合调试双核工程的三种方法(IAR篇)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT1170下在线联合调试双核工程的方法(基于IAR). 前段时间痞子衡写过一篇<双核i.MXRT1170之单独在线调试从 ...

  8. 创建进程,join方法,进程对象相关属性和方法,僵尸进程和孤儿进程,守护进程,互斥锁

    创建进程 在python中提供了一个multiprocessing模块可以帮助我们使用多进程解决问题.在multiprocessing 模块中有一个类Process. from multiproces ...

  9. 玩转ASP.NET 6.0框架-序言

    ASP.NET Core是微软提供的强大的web框架,它有很多潜在的强大而有用的功能. 本专栏的目标是帮助您把框架的隐藏能力最大限度地发挥出来,让您能够按需定制ASP NET Core框架.本专栏提供 ...

  10. 操作系统:Tails

    Tor是一个网络 如今,Tor浏览器可能是Tor的代言人,但Tor的真正力量在于Tor网络.大家都知道,"Tor"实际上是"The Onion Router"( ...