
1.Kotlin中创建集合
Kotlin没有采用自己的集合类,因为使用标准的Java集合类,Kotlin可以更容易与Java代码交互
val set = hashSetOf("渣渣灰","古天乐","刘青云")
val list = arrayListOf("enjoy","your","time")
val map = hashMapOf(1 to "one",2 to "two",3 to "three")
>>> println(set.javaClass)
class java.util.HashSet
>>> println(list.javaClass)
class java.util.ArrayList
>>> println(map.javaClass)
class java.util.HashMap
但竟然会发现还可以这样写
val strings = listOf("first","second","third")
>>> println(strings.last()) //这个函数其实是扩展的,下文会有介绍
third
2.扩展函数和属性
扩展函数很简单,它就是类的一个成员函数,只不过在类的外边
package strings
/**
* 该扩展函数来计算字符串的最后一个字符
*/
fun String.lastChar(): Char = this.get(this.length - 1)

1.使用扩展函数
import strings.lastChar
val c = "Kotlin".lastChar()
//或者使用*
import strings.lastChar
val ch = "Kotlin".lastChar()
//再或者
import strings.lastChar as last
val c = "Kotlin".last()
Java中调用Kotlin的扩展函数,假定它声明在一个叫做StringUtil.kt的文件中
/* Java */
char c = StringUtilKt.lastChar("Java")
这个扩展函数被声明在顶层函数,所以它会被编译成一个静态函数,Java中直接静态导入就行了
再来扩展一个函数作为工具函数使用
/**
* 格式化时间,输入为秒
*/
fun durationFormat(duration: Long): String {
val minute = duration / 60
val second = duration % 60
return if (minute <= 9) {
if (second <= 9) {
"0$minute' 0$second''"
} else {
"0$minute' $second''"
}
} else {
if (second <= 9) {
"$minute' 0$second''"
} else {
"$minute' $second"
}
}
}
>>> println(durationFormat(189))
03'09''
>>> println(durationFormat(15))
00'15''
扩展函数就是静态函数的一个高效语法糖
2.不可重写的扩展函数
open class View {
open fun click() = println("View clicked")
}
class Button: View() {
override fun click() = println("Button clicked")
}
>>> val view: View = Button()
>>> view.click()
Button clicked
上面这段代码的输出和Java一致,但对于扩展函数来说就不是了
fun View.showOff() = println("I'm a view!")
fun Button.showOff() = println("I'm a button!")
>>> val view: View = Button()
>>> view.showOff()
I'm a view!
怎么会出现这种呢,可能与想象不太一样,那看下反编译成.class的内容

>>> View view = new Button()
>>> ExtensionKt.showOff(view)
I'm a view!
3.扩展属性
和扩展函数一样,扩展属性也像接收者的一个普通成员属性一样
val String.lastChar: Char
get() = this.get(this.length - 1)
var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value) {
this.setCharAt(length - 1, value)
}
//使用扩展属性
>>> println("Kotlin".lastChar)
n
>>> val sb = StringBuilder("Kotlin?")
>>> sb.lastChar = '!'
>>> println(sb)
Kotlin!
Java中使用扩展属性的时候,应该显式的使用getter:
StringUtil.getLastChar("渣渣灰");
3.处理集合:可变参数、中缀调用和库的支持
1.可变参数
val actors = listOf("周星星","fa哥","王祖贤")
很明显,这个初始化list的时候参数个数是可变的,来看下Kotlin的库函数是如何实现的
/**
* Returns a new read-only list of given elements. The returned list is serializable (JVM).
* @sample samples.collections.Collections.Lists.readOnlyList
*/
public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()
使用到了泛型,泛型跟Java的泛型类似,注释中也提到返回的list只读且是序列化的,怎么让它长度可变呢,看下边的
val actors1 = mutableListOf<String>()
actors1.add("杜sir")
val actors2 = arrayListOf<String>() //其实就是Java中的ArrayList
actors2.add("肥猫")
看下Kotlin中这两个函数是如何实现的
/**
* Returns an empty new [MutableList].
* @sample samples.collections.Collections.Lists.emptyMutableList
*/
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> mutableListOf(): MutableList<T> = ArrayList()
/**
* Returns a new [MutableList] with the given elements.
* @sample samples.collections.Collections.Lists.mutableList
*/
public fun <T> mutableListOf(vararg elements: T): MutableList<T>
= if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
/**
* Returns an empty new [ArrayList].
* @sample samples.collections.Collections.Lists.emptyArrayList
*/
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> arrayListOf(): ArrayList<T> = ArrayList()
/**
* Returns a new [ArrayList] with the given elements.
* @sample samples.collections.Collections.Lists.arrayList
*/
public fun <T> arrayListOf(vararg elements: T): ArrayList<T>
= if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
可以看出其实最终都是使用了Java集合的ArrayList()
2.中缀调用
使用mapOf函数创建map:
val map = mapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
这行代码的to不是内置的结构,而是一种特殊的函数调用,被称为中缀调用,一下两种方式是等价的:
1.to("one")
1 to "one"
要使用中缀符号调用函数,需要使用infix修饰符来标记它,下面是一个简单的to函数的声明:
infix fun Any.to(other: Any) = Pair(this, other)
to函数会返回一个Pair类型的对象,Pair是Kotlin标准库的类,表示一对元素,可以用Pair的内容来初始化两个变量:
val (number,name) = 1 to "one"
这个功能成为结构,再来看下mapOf函数的声明:
fun <K,V> mapOf(vararg values: Pair<K,V>): Map<K,V>
集合的解构可以如下使用
for((index,element) in collection.withIndex()) {
println("$index: $element")
}
4.字符串和正则表达式的处理
处理一个字符串的分割
>>> println("12.345-6.A".split("\\.|-".toRegex())
[12, 345, 6, A]
//也可以这样用
>>> println("12.345-6.A".split(".","-")
[12, 345, 6, A]
再看另一个例子的两种不同实现,分割文件路径"/Users/zhazhahui/kotlin/info.txt",第一种如下
//1.使用String的扩展函数来解析文件路径
fun parsePath(path: String) {
val directory = path.substringBeforeLast("/")
val fullName = path.substringAfterLast("/")
val fileName = fullName.substringBeforeLast(".")
val extension = fullName.substringAfterLast(".")
println("Dir: $directory, name: $fileName, ext: $extension")
}
>>> parsePath("/Users/zhazhahui/kotlin/info.txt")
Dir: /Users/zhazhahui/kotlin, name: info, ext: txt
第二种使用正则表达式
//2.使用正则表达式切割
fun parsePath(path: String) {
val regex = """(.+)/(.+)\.(.+)""".toRegex()
val matchResult = regex.matchEntire(path)
if (matchResult != null) {
//解构,与Pair的解构一致
val (directory, fileName, extension) = matchResult.destructured
println("Dir: $directory, name: $fileName, ext: $extension")
}
}
这里正则表达式写在一个三重引号的字符串中。在这样的字符串中,不需要对任何字符进行转义,包括反斜线,所以可以用. 而不是\. 来表示点,正如写一个普通字符串的字面值。在这个正则表达式中:第一段(.+)表示目录,/表示最后一个斜线,第二段(.+) 表示文件名,. 表示最后一个点,第三段(.+) 表示扩展名。
三重引号字符串的目的,不仅在于避免转义字符,而且使它可以包含任何字符,包括换行符。它提供了一种更简单的方法,从而可以简单的把包含换行符的文本嵌入到程序中:
val kotlinLogo = """|//
.|//
.|/ \
""".trimMargin(".")
>>> print(kotlinLogo)
|//
|//
|/ \
多行字符串包含三重引号之间的所有字符,包括用于格式化代码的缩进。如果要更好的表示这样的字符串,可以去掉缩进(左边距)。为此,可以向字符串内容添加前缀,标记边距的结尾,然后调用trimMargin 来删除每行中的前缀和前面的空格。在这个例子中使用了. 来作为前缀。
5.局部函数和扩展
先看一个简单的保存用户
class User(val id: Long,val name: String,val address: String)
fun saveUser(user: User) {
if (user.name.isEmpty()) {
throw IllegalArgumentException("Can't save user ${user.id}: empty Name")
}
if (user.address.isEmpty()) {
throw IllegalArgumentException("Can't save user ${user.id}: empty Address")
}
//保存user到数据库
}
这里才校验了两个属性,但看起来已经造成了冗余,看下Kotlin的局部函数如何处理这个
fun saveUser(user: User) {
fun validate(value: String,fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException("Can't save user ${user.id}: empty $fieldName")
}
}
validate(user.name,"Name")
validate(user.address,"Address")
//保存user到数据库
}
可以继续改进,把验证逻辑放到User类的扩展函数中
fun User.validateBeforeSave() {
fun validate(value: String,fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException("Can't save user ${id}: empty $fieldName")
}
}
validate(name,"Name")
validate(address,"Address")
}
fun save(user: User) {
user.validateBeforeSave()
//保存user到数据库
}