1 非空类型与可空类型
1.1 非空类型
为了让代码更安全,Kotlin相比Java新增了非空类型与可空类型的概念。
普通的String
类型在Kotlin中属于非空类型non-null type String
,不能复制为null
。
fun test0() {
val nonNull: String = ""
// nonNull = null
var length1 = nonNull.length
}
我们将赋值为null
的代码行注释,并直接读取nonNull
的length
,因为nonNull
是非空类型,所以读取length
的代码是完全安全的,绝对不会出现空指针异常。这对提升代码安全性大有益处。
1.2 可空类型
如果我们需要一个可空类型呢?
请使用String?
类型
如上图,可空类型可以被赋值为null
。但是当我们尝试直接去读取nullable
变量的length
属性的时候,编译器开始报错,如下图。
编译器告诉我们:String?
类型只能进行安全调用?.
或者对变量进行强行非空断言后调用非空类型的属性和方法。
1.2.1 安全调用
对上述代码进行更改,按照提示采用安全调用,编译通过了。但此时的length2
变量自动推导的类型是Int?
而不是Int
,如下图。
可知,当可空类型变量nullable
是null
时,length2: Int?
也是null
,且此时读取length
属性的操作不会进行,保证了代码的安全性。
1.2.2 强行非空断言
当我们确定可空变量的值不是null
时,我们可以使用!!.
对变量进行强行非空断言,然后调用属性或方法。这是非常不推荐的危险操作!除非必要,不要使用,养成良好的编程习惯。
注意!经过强行非空断言的代码行,
length2
是Int
类型,而不是Int?
,如下图。
1.2.3 elvis表达式
面对可空类型,我们使用的最多的当然是安全调用,这里需要介绍elvis表达式。
elvis表达式用在安全调用的场景中,当安全调用的可空类型变量是null
时,elvis表达式将输出?:
后面的值,充当替补值。例如上图中的length2
将被赋值为0
。
当然,此时无论nullable
是否为空,length2
都能得到一个Int
值,因此,length2
将被推断为Int
类型,如下图,这很好理解。
1.3 String与String?的关系
如图可见,将String
类型变量赋值给String?
类型变量是OK的,但是将String?
类型变量赋值给String
类型变量是NOT OK的。根据里氏替换原则,可知,String?是String的父类。
2 平台类型
我们在Java中定义一个简单的Person
类:
//Java
package imooc.chapter_4;
public class Person {
public String name;
public String gender;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
然后我们在Kotlin中创建一个Person
类的对象bob
。
//Kotlin
package imooc.chapter_4
fun main() {
val bob = Person("Bob")
var name = bob.getName()
var gender = bob.getGender()
var length = gender?.length
}
此时,变量name
、gender
的推导类型为String!
,如下图。
String!
类型即为平台类型。
此时,bob
对象的gender
字段还未set,故为null
,但是Kotlin不知道,因此直接如图调用将会空指针报错。
此例中的平台为Java,故该String!
为Java中的String
类。平台类型为空与否,是否支持为空都是Kotlin无法确定的,因此需要谨慎使用。可以考虑安全访问和elvis表达式。