?
对于如下Java函数,可传递null或者值为null的String
int strLen(String s) {
    return s.length();
}
 
而在Kotlin中,如下函数不能传递null或值为null的String,否则会在编译期报错,保证了永远不会在运行时报空指针异常
fun strLen(s: String) = s.length
 
如果接收null,需要在类型名称后面加上?标记
fun strLen(s: String?): Int =
    if (s != null) s.length else 0
 
?.
把一次null检查和方法调用合并成一个操作,为空时返回null
fun printAllCaps(s: String?) {
    val allCaps: String? = s?.toUpperCase()
    println(allCaps)
}
 
调用结果也是可空的,如下打印ABC、null
println("abc")
println(null)
 
?:
同上,但为空时返回冒号后面的默认值,如下s为空返回""
fun foo(s: String?) {
    val t: String = s ?: ""
}
 
as?
把值转换成指定的类型,若转换失败则返回null
class Person(val name: String) {
    override fun equals(other: Any?): Boolean {
        val otherPerson = other as? Person ?: return false
        return otherPerson.name == name
    }
}
 
!!
把值转换成非空类型,若为空则抛出异常,堆栈信息只会表明异常发生在哪一行代码,而不是哪一个表达式,应避免在同一行使用多个!!
fun ignoreNulls(s: String?) {
    val sNotNull: String = s!!
    println(sNotNull.length)
}
 
let
当把可空值作为实参传递给非空值的函数时,可使用let
fun sendEmailTo(email: String) {
    println("sendEmailTo")
}
 
let只会在值非空时调用
val email1: String? = "AAA"
email1?.let { sendEmailTo(it) }
val email2: String? = null
email2?.let { sendEmailTo(it) }
 
延迟初始化
如下每次使用myService变量,都需要判空
class MyService {
    fun getService(): String = "Service"
}
class Test {
    private var myService: MyService? = null
    
    fun setUp() {
        myService = MyService()
    }
    
    fun action() {
        myService?!.getService()
    }
}
 
可使用lateinitb表明变量可以延迟初始化,不用先置为null,省去判空条件
class MyService {
    fun getService(): String = "Service"
}
class Test {
    private lateinit var myService: MyService
    fun setUp() {
        myService = MyService()
    }
    fun action() {
        myService.getService()
    }
}
 
可空类型的扩展函数
String的扩展函数isNullOrBlank()判断当前字符串是否为空或者空白,this可能为null,若不为null,则可以安全调用isBlank()
public inline fun CharSequence?.isNullOrBlank(): Boolean {
    return this == null || this.isBlank()
}
 
类型参数的可空行
Kotlin所有泛型类和泛型函数的类型参数默认都是可空的,如下函数可传递null
fun <T> printHashCode(t: T) {
    println(t?.hashCode())
}
 
要使类型参数非空,必须指定一个非空的上界
fun <T : Any> printHashCode(t: T) {
    println(t.hashCode())
}
 
可空性和Java
Java使用@Nullable表示可为空,@NotNull表示非空,当不存在注解时,会作为Kotlin的平台类型(即不知道可空性的类型),由开发者自己处理
基本数据类型
- Kotlin非空的基本数据类型转换为Java的基本数据类型
 - Kotlin可空的基本数据类型转换为Java的基本数据包装类
 
数字转换
Kotlin不会自动转换类型,需要显示调用转换函数
val i = 1
val l: Long = i.toLong()
 
Any、Any?
Any是所有类型的父类,对应于Java的Object
Unit
当函数未声明返回值类型时,默认为Unit,对应于Java的void
fun f() {
    
}
fun fu(): Unit {
}
 
Unit可以作为泛型的类型参数,而void不行,如下使用Unit作为返回值,不用显示return
interface Processor<T> {
    fun process(): T
}
class NoResultProcessor : Processor<Unit> {
    override fun process() {
        
    }
}
 
Nothing
Nothing没有任何值,只有被当作函数返回值,或者当作泛型函数返回值的类型参数才有意义
fun fail(msg: String): Nothing {
    throw IllegalStateException(msg)
}
 
集合
可空性和集合
需要注意,集合可空性和集合元素可空性是不一样的
fun addValidNumbers(numbers: List<Int?>) {
    var validNumbers = 0
    for (number in numbers) {
        if (number != null) {
            validNumbers += number
        }
    }
}
 
可使用库函数优化
fun addValidNumbers(numbers: List<Int?>) {
    var validNumbers = numbers.filterNotNull().sum()
}
 
只读集合和可变集合
Collection中的方法都是读取集合的操作,MutableCollection才包含写集合的操作
fun <T> copyElements(source: Collection<T>, target: MutableCollection<T>) {
    for (item in source) {
        target.add(item)
    }
}
 
只读集合不一定是不可变的,可能同时有MutableCollection引用它
Koltin集合和Java
一种Java集合在Kotlin都有只读和可变两种表示,不能保证传递给Java的集合不会被修改及添加null

数组
- arrayOf() 创建包含指定参数的数组
 - arrayOfNulls() 创建可空数组
 - Array() 调用Lambda表达式构建数组元素
 
val letters = Array<String>(26) { i -> ('a' + i).toString() }
 
数组的类型参数会被成为对象类型,若要生成基本数据类型的数组,可以使用xxxArray()
val a = IntArray(5)
val b = intArrayOf(0, 1, 2, 3, 4)
val c = IntArray(5) { i -> (i + 1) }
println(a.joinToString(" "))	// 0 0 0 0 0 
println(b.joinToString(" "))	//0 1 2 3 4
println(c.joinToString(" "))	//1 2 3 4 5
 
可对装箱的数组或集合转为基本数据类型的数组
val list = listOf<Int>(1, 2, 3)
val array = list.toIntArray()










