?
对于如下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
printAllCaps("abc")
printAllCaps(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()
}
}
可使用lateinit表明变量可以延迟初始化,不用先置为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是所有非空类型的父类,而Any?则是Any的父类型
需要注意的是Any和Any?并非继承关系,但需要使用Any?的地方可以传入Any,称为子类型化关系,其是一种类型的替代关系
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没有任何值,只有被当作函数返回值,或者当作泛型函数返回值的类型参数才有意义,任何返回值为Nothing的表达式之后的语句都是无法执行的,即Kotlin的return和throw都是返回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()