可空类型
* 类型就是数据的分类。决定了该类型可能的值,以及在该类型值上可以完成的操作。
* 与Java不同,Kotlin对可空类型的显示的支持。可空类型是Kotlin类型系统中帮助避免NullPointException错误的特性。
* 这是一种指出你的程序中那些变量和属性允许为null的方式。
* 可空类型和非可空类型的对象在运行时没有什么区别,可空类型并不是非空类型的包装。所有检查都是在编译时期,所以Kotlin的可空类型并不会在运行时带来额外的开销。
先来看看Java中的空指针是怎么造成的
private int stringLen(String s){ retrun s.length }
当传入的参数s为null的时候就会发生空指针异常、。
* 使用Kotlin声明同样的方法不接收可能为null的参数,因为当如果传入可能会null的参数在编译器就会被标记成错误。
* 这样就保证了strLen函数永远不会在运行的时候抛出NullPointException
fun strLen(s: String) = s.length //Kotlin默认s是不可为null的参数如果你传入一个可能为null参数,编译的时候就会报错
如果你想要声明一个接收参数可能为null的参数,就需要将这个参数声明为可空类型的参数,而可控类型的参数声明起来也特别简单,只需要咋参数的类型之后加上?
* ?可以加载任意类型的后面来表示这个类型的变量可以存储null引用。如String? Int? MyCustomType?
* Type ? = Type or null
* 没有问号的类型表示这种类型的变量不能存储为null.这说明说有的常见类型都是默认为非空的,除非显示地把它标记为可空的
* 可空类型的变量不能直接调用其方法、不能把它赋值给费空类型的变量、也不能把可空类型的值传递给拥有非空类型的参数的函数。
* 再与null 进行比较之后,编译器就会记住,并且在这次比较发生的作用域内把这个值当错非空来对待
fun nullAbleStrLen(s: String?): Int = if (null != s) s.length else 0
安全调用符
* Kotlin提供了一种非常有用的工具:安全调用运算符"?"
* 它允许你把一次对null的检查和一次方法调用合并成一个操作。例如:s?.toUpperCase() 等同于 if(s!=null) s.toUpperCase() else null
* 如果你试图调用一个非空值的方法,这次方法调用就会被正常执行。但是如果是null 这次调用就不会发生,而整个表达式的值就为null
fun printAllCaps(s: String?) { //这里要注意 因为后面的表达式返回值可能为空,前面的对象类型声明的时候就要声明成可空类型 val allCaps: String? = s?.toUpperCase() println(allCaps) }
* 安全调用不光可以调用方法,也能用来访问属性
* Kotlin 支持多个安全调用链接再一起使用
fun managerName(employee: Employee): String? = employee?.name
class Address(val streetAddress: String, val zipCode: Int, val city: String, val country: String) class Company(val name: String, val address: Address?) class Person(val name: String, val company: Company?) fun Person.countryName(): String { val country = this.company?.address?.country return if (null != country) country else "Unknown" }
Elvis 运算符“?:”
* Elvis 运算符“?:”
* Kotlin 有方便的运算符来提供null的默认值。它被称作Elvis运算符(或 null合并运算符) -> ?:
*
* Elvis运算符接收两个运算数,如果第一个运算数不为null,运算结果就是第一个运算数,如果第一个运算数为null,运算结果就是第二个运算数
fun foo(s: String?): String { val t: String = s ?: " " return t }
* Elvis运算符经常和安全调用运算符一起使用,用一个值代替对null对象调用方法时返回的null
fun strLenSafe(s: String?): Int = s?.length ?: 0
* 上面获取countryName的方法就可以使用Elvis进一步简化
fun Person.getCountryName(): String = this.company?.address?.country ?: "Unknown"
* Kotlin中 Elvis和return 、 throw 表达式结合再一起使用会更加好使
fun printShippingLabel(person: Person) { val address = person?.company?.address ?: throw IllegalArgumentException("No address") with(address) { println(streetAddress) println("$zipCode $city $country") } }
安全转换 "as?"
* 常规的as运算符和Java中的类型转换一样转换对象的类型,如果被转换的值不是你试图转化的类型,就会抛出ClassCastException。Kotlin提供了一种安全的类型转化操作 as?
* as?运算符尝试把值转换成指定的类型,如果值不是合适的类型就返回null ,经常会和Elvis “?:” 一起使用。
class Person1(val firstName: String, val lastName: String) { override fun equals(other: Any?): Boolean { //类型检查如果不匹配就返回false val otherPersion1 = other as?Person1 ?: return false //安全检查之后变量会被只能转换为Person1类型 return otherPersion1.firstName == firstName && otherPersion1.lastName == lastName } override fun hashCode(): Int { return firstName.hashCode() * 37 + lastName.hashCode() } }
非空断言 “!!”
* 非空断言是Kotlin提供的一种简单直率的处理可空类型值的工具。
* 它使用双叹号表示,可以把任何值转化为非空类型,如果要转换的值是null,则会抛出NullPointException异常
fun ignoreNulls(s: String?) { val sNotNull: String = s!! println(sNotNull) }
//注意当使用!!并且它的结果是异常时,异常调用栈的跟踪信息只会表明异常发生在哪一行,不会表明异常时由那个值引起的,所以尽量避免在一行使用多个非空断言
// person!!.company!!.address!!.name 出现异常的时候不好查找引起的对象