Kotlin 知识梳理系列文章
Kotlin 知识梳理(1) - Kotlin 基础
Kotlin 知识梳理(2) - 函数的定义与调用
Kotlin 知识梳理(3) - 类、对象和接口
Kotlin 知识梳理(4) - 数据类、类委托 及 object 关键字
Kotlin 知识梳理(5) - lambda 表达式和成员引用
Kotlin 知识梳理(6) - Kotlin 的可空性
Kotlin 知识梳理(7) - Kotlin 的类型系统
Kotlin 知识梳理(8) - 运算符重载及其他约定
Kotlin 知识梳理(9) - 委托属性
Kotlin 知识梳理(10) - 高阶函数:Lambda 作为形参或返回值
Kotlin 知识梳理(11) - 内联函数
Kotlin 知识梳理(12) - 泛型类型参数
一、本文概要
本文是对<<Kotlin in Action>>
的学习笔记,如果需要运行相应的代码可以访问在线环境 try.kotlinlang.org,这部分的思维导图为:
二、在 kotlin 中创建集合
在kotlin
中,创建HashSet
、ArrayList
和HashMap
的方法如下:
通过打印这些集合的类型,可以看到是采用的标准的
Java
集合类:这么做的原因,是因为使用标准的
Java
集合使kotlin
可以更容易地与Java
代码交互。当从Kotlin
调用Java
函数的时候,不用转换它的集合类来匹配Java
的类,反之亦然。
在这些集合对象上,我们除了可以使用Java
当中定义的基本函数以外,还可以使用kotlin
提供的扩展方法,例如下面的last
和max
:
运行结果为:
三、让函数更好调用
下面,我们定义一个函数,它的作用的是打印集合当中的元素,并指定元素之间添加分隔符、前缀和后缀:
运行结果为:
3.1 命名参数
在上面的例子中,我们指定了分隔符、前缀和后缀三个参数,但是对于这个函数的使用者来说,如果不去看这些函数的声明,很难看出这些String
类型的含义,这时候就可以使用 命名参数 的方法来调用,这有两点好处:
- 增加函数的可读性
- 以想要的顺序指定需要的参数
下面,我们使用命名参数,并在不改变函数定义的情况下,改变传入参数的顺序,也可以得到和上面相同的运行结果:
对于命名参数,有以下几点需要注意:
- 如果在调用一个函数时,指明了一个参数的名称,那它之后的所有参数都要表明名称。
- 当调用
Java
函数时,不能使用命名参数。
3.2 默认参数值
在kotlin
中,可以在声明函数的时候,指定参数的默认值,这样就可以避免创建重载的函数,例如上面的例子,我们可以在 定义函数 时,指定三个String
的默认值,而在 调用函数 的时候,如果没有传递这些参数,那么将会采用默认值:
运行结果为:
但是要注意:
- 如果使用常规的调用语法时,必须按照函数声明中定义的参数顺序来给定参数,可以省略的只有排在末尾的参数。
-
如果使用命名参数,可以省略中间的一些参数,也可以以你想要的任意顺序只给定你需要的参数,例如我们只修改前缀和后缀,分隔符仍然采用默认值:
运行结果为:
3.3 顶层函数和属性
在Java
中,所有的代码都需要写作类的函数,但是在项目中,很多代码并不能归属到类中,这时候我们一般会定义一个xxUtils
类,并在其中声明static
的静态方法。
在kotlin
中,我们可以把这些函数直接放到代码文件的顶层,不用从属于任何的类,这些放在文件顶层的函数仍然是包内的成员,如果你需要从包外访问它,则需要import
,但不再需要额外包一层。
3.3.1 在 Java 中调用顶层函数
如果我们想要在Java
中调用这些顶层函数,则需要通过Kotlin
根据包含函数的文件的名称生成的类,例如我们有两个文件
-
KotlinMethod.kt
:
fun kotlinFunc() {}
-
JavaCallKotlin.java
:
public class JavaCallKotlin {
public JavaCallKotlin() {
KotlinMethodKt.kotlinFunc();
}
}
前者包含一个顶层函数,那么在Java
中,就会根据该文件名生成一个{文件名}Kt
的类型,再通过这个类来调用这个顶层函数。
如果不想使用默认的类名,可以在.kt
文件的开头加上@file:JvmName("类名")
的注解。
3.3.2 在 Java 中调用顶层属性
和函数一样,属性也可以放到文件的顶层。默认情况下,顶层属性和其他任意的属性一样,是通过访问器暴露给Java
使用的(如果是val
就只有一个getter
,如果是var
就对应getter
和setter
)。如果想要把一个常量以public static final
的属性暴露给Java
,可以使用const
来修饰它。
package com.demo.lizejun.kotlinsample.chapter1
//不可变。
val kotlinVal = "kotlinValue"
//可变。
var kotlinVar = "kotlinVariable"
//常量。
const val kotlinConst = "kotlinConst"
//顶层函数。
fun kotlinFunc() {}
在Java
中,分别通过以下几种方式来访问或者修改这几个顶层属性:
package com.demo.lizejun.kotlinsample.chapter3;
import com.demo.lizejun.kotlinsample.chapter1.KotlinMethodKt;
public class JavaCallKotlin {
public JavaCallKotlin() {
KotlinMethodKt.kotlinFunc();
//不可变。
KotlinMethodKt.getKotlinVal();
//可变。
KotlinMethodKt.setKotlinVar("newKotlinVar");
KotlinMethodKt.getKotlinVar();
//常量。
String kotlinConst = KotlinMethodKt.kotlinConst;
}
}
四、扩展函数和属性
扩展函数 其实是一个类的成员函数,只不过它定义在类的外面,我们所需要做的,就是在声明扩展函数的时候,把需要扩展的类或者接口的名称,放到它的前面,用来调用这个扩展函数的对象,就叫做 接收者对象。
在扩展函数中,可以直接访问被扩展的类的其它方法和属性,就好像是在这个类自己的方法中访问它们的一样,但是扩展函数不允许你打破它的封装性,扩展函数不能访问私有的或者是受保护的成员。
4.1 扩展函数的定义和使用
下面我们给String
类添加一个扩展函数,返回它的最后一个字符:
运行结果为:
4.2 在 Java 中使用扩展函数
如果需要在Java
中调用扩展函数,那么把接收者对象作为第一个参数传进去即可:
//接收者对象作为第一个参数。
char lastChar = KotlinMethodKt.last("Kotlin");
4.3 不能重写的扩展函数
这里假设我们有两个类,View
和Button
,其中Button
继承于View
,我们给这两个类都添加一个名为showOff
的扩展函数。
运行结果为:
尽管实际上这个变量是一个
Button
的对象,但是Kotlin
会把扩展函数当做静态函数来对待,因此 扩展函数不存在重写。
4.4 扩展属性
扩展属性提供了一种方法,用来扩展类的API
,可以用来访问属性,用的是属性语法而不是函数的语法,尽管他们被称为属性,但它们没有任何状态,因为没有合适的地方来存储它们。
现在,我们给StringBuilder
添加一个可读写的属性lastChar
,用于获取或者改变它的最后一个字符,包含以下几点要素:
- 扩展属性以
var/val
关键字开头 - 指定扩展属性的名字、类型
- 如果是
var
那么提供get()/set(value : T)
方法,而如果是val
属性,那么提供get()
方法,其中T
为属性的类型。
运行结果为:
五、可变参数、中缀调用和库的支持
5.1 可变参数
使用关键字 vararg,可以用来声明一个函数将有可能有任意数量的参数,下面例子中,我们定义一个可以接收可变数量Int
类型的函数,之后和在Java
中一样,它会被转换为[I
的整型数组类型:
运行结果为:
而如果我们已经将参数打包成一个数组,这时候如果想要将它传递给一个接收可变参数的函数,那么需要先通过
*
操作符进行解包:运行结果为:
5.2 中缀调用
中缀调用 不是特殊的内置结构,而是一种特殊的函数调用。在中缀调用中,没有添加额外的分隔符,函数名称是直接放在目标对象名称和参数之间。例如我们声明了一个to
函数。
//一般函数调用。
1.to("One")
//中缀调用。
1 to "One"
中缀调用可以与 只有一个参数的函数 一起使用,无论是普通的函数还是扩展函数,要允许使用中缀符号调用函数,需要使用infix
修饰符来标记它,下面是一个创建Person
的函数,我们采用了扩展函数的方法,这里的Any
是Kotlin
中所有类的父类,和Java
中的Object
相同:
运行的结果为:
六、字符串处理
6.1 分割字符串
在Java
中,我们会使用split
来分割字符串,它接受一个正则表达式作为参数。但是当我们想分割"."
时,会得到一个空的数组,因为.
号表示任何字符的正则表达式。
而在kotlin
中,它提供了一个接受Regex
类型的重载函数,这样确保了当有一个字符串传递给这些函数的时候,不会被当做正则表达式,我们可以使用扩展函数toRegex
将字符串转换为正则表达式。
运行结果为:
6.2 正则表达式
假设有下面这个字符串
/Users/yole/kotlin-book/chapter.adoc
我们需要通过这个字符串获取到chapter.adoc
的目录、文件名和扩展名,如果使用扩展函数,那么代码如下:
运行结果为:
下面是使用正则表达式的做法:
这个正则表达式将一个路径分为三个由斜线和点分隔的组:
-
.
模式从字符串的一开始就进行匹配,所以第一组(.+)
包含最后一个斜线之前的子串,这和子串包含所有前面的斜线,因为它们匹配”任何字符“的模式。 - 第二组包含最后一个点之前的子串
- 第三组包含剩余部分
七、局部函数
在Java
的一个函数当中,有可能存在重复代码,例如在注册模块中,可能需要校验输入的多个字段是否有效,那么校验的逻辑就可以提取出一个函数,而Kotlin
就提供了一种方法:可以在函数中嵌套这些提取的函数,局部函数定义方式和普通函数是相同的。
运行的结果为:
更多文章,欢迎访问我的 Android 知识梳理系列:
- Android 知识梳理目录:http://www.jianshu.com/p/fd82d18994ce
- 个人主页:http://lizejun.cn
- 个人知识总结目录:http://lizejun.cn/categories/