Kotlin 空值安全
Kotlin 的类型系统旨在消除代码中空引用的危险,因为它是十亿美元的错误。 NullPointerExceptions由程序在运行时抛出,有时会导致应用程序失败或系统崩溃。
如果有人使用Java或其他具有空引用概念的语言进行编程,那么他一定经历过代码中的NullPointerException 。如果 Kotlin 编译器在没有执行任何其他语句的情况下发现任何空引用,它也会抛出 NullPointerException。
NullPointerException 的可能原因如下:
- 显式调用 throw NullPointerException()
- 使用!!运算符
- 一些与初始化有关的数据不一致,例如未初始化的 this 作为参数传递。
- Java互操作,例如尝试访问空引用上的成员、具有不正确可空性的泛型类型。
Kotlin 中的可空和不可空类型 –
Kotlin 类型系统区分了两种类型的引用,可以包含 null(可为 null 的引用)和不能包含的引用(非 null 引用)。
String 类型的变量不能保存null 。如果我们尝试将 null 分配给变量,则会出现编译器错误。
var s1: String = "Geeks"
s1 = null // compilation error
为了让一个变量保持为空,我们可以将一个变量声明为可以为空的字符串,写成String?
var s2: String? = "GeeksforGeeks"
s2 = null // ok
print(s2)
现在,如果我们要访问字符串s1 的长度,它保证不会抛出 NPE,所以我们可以有把握地说:
val l = s1.length
但是如果我们要访问字符串s2 的长度,那就不安全了,编译器会报错:
val l = s2.length // error: variable 's2' can be null
不可为空类型的 Kotlin 程序 –
Kotlin
fun main(args: Array){
// variable is declared as non-nullable
var s1 : String = "Geeks"
//s1 = null // gives compiler error
print("The length of string s1 is: "+s1.length)
}
Kotlin
fun main(args: Array) {
// variable is declared as nullable
var s2: String? = "GeeksforGeeks"
s2 = null // no compiler error
println(s2.length) // compiler error because string can be null
}
Kotlin
fun main(args: Array) {
// variable declared as nullable
var s: String? = "GeeksforGeeks"
println(s)
if (s != null) {
println("String of length ${s.length}")
} else {
println("Null string")
}
// assign null
s = null
println(s)
if (s != null) {
println("String of length ${s.length}")
} else {
println("Null String")
}
}
Kotlin
fun main(args: Array) {
// variable declared as nullable
var firstName: String? = "Praveen"
var lastName: String? = null
println(firstName?.toUpperCase())
println(firstName?.length)
println(lastName?.toUpperCase())
}
Kotlin
fun main(args: Array) {
// created a list contains names
var stringlist: List = listOf("Geeks","for", null, "Geeks")
// created new list
var newlist = listOf()
for (item in stringlist) {
// executes only for non-nullable values
item?.let { newlist = newlist.plus(it) }
}
// to print the elements stored in newlist
for(items in newlist){
println(items)
}
}
Kotlin
fun main(args: Array) {
// created a list contains names
var stringlist: List = listOf("Geeks","for", null, "Geeks")
// created new list
var newlist = listOf()
for (item in stringlist) {
// executes only for non-nullable values
item?.let { newlist = newlist.plus(it) }
item?.also{it -> println(it)}
}
}
Kotlin
fun main(args: Array) {
// created a list contains names
var stringlist: List = listOf("Geeks","for", null, "Geeks")
// created new list
var newlist = listOf()
for (item in stringlist) {
// executes only for non-nullable values
item?.run { newlist = newlist.plus(this) } // this reference
item?.also{it -> println(it)}
}
}
Kotlin
fun main(args: Array) {
var str : String? = "GeeksforGeeks"
println(str?.length)
str = null
println(str?.length ?: "-1")
}
Kotlin
fun main(args: Array) {
var str : String? = "GeeksforGeeks"
println(str!!.length)
str = null
str!!.length
}
输出:
The length of string s1 is: 5
在这里,如果我们尝试将null分配给一个不可为空的变量,那么它会给出编译器时间错误。但是,如果我们尝试访问字符串的长度,那么它保证不会通过 NullPointerException。
可空类型的 Kotlin 程序 -
科特林
fun main(args: Array) {
// variable is declared as nullable
var s2: String? = "GeeksforGeeks"
s2 = null // no compiler error
println(s2.length) // compiler error because string can be null
}
输出:
Error:(8, 15) Kotlin: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
在这里,我们可以轻松地将 null 分配给可为 null 的类型变量。但是我们应该使用安全运算符来获取字符串的长度。
检查条件中的空值-
检查空引用的最常用方法是使用 if-else 表达式。我们可以显式检查变量是否为空,并分别处理这两个选项。
Kotlin 检查空值条件的程序 -
科特林
fun main(args: Array) {
// variable declared as nullable
var s: String? = "GeeksforGeeks"
println(s)
if (s != null) {
println("String of length ${s.length}")
} else {
println("Null string")
}
// assign null
s = null
println(s)
if (s != null) {
println("String of length ${s.length}")
} else {
println("Null String")
}
}
输出:
GeeksforGeeks
String of length 13
null
Null String
请注意,我们使用if-else块来检查可空性。如果字符串包含 null 则执行 if 块,否则执行 else 块。
安全呼叫运算符(?.) –
Null 比较很简单,但嵌套 if-else 表达式的数量可能会很麻烦。所以,Kotlin 有一个 Safe call运算符,?。这降低了这种复杂性并仅在特定引用包含非空值时执行操作。它允许我们将空检查和方法调用组合在单个表达式中。
以下表达式:
firstName?.toUpperCase()
相当于:
if(firstName != null)
firstName.toUpperCase()
else
null
Kotlin 使用安全运算符的程序——
科特林
fun main(args: Array) {
// variable declared as nullable
var firstName: String? = "Praveen"
var lastName: String? = null
println(firstName?.toUpperCase())
println(firstName?.length)
println(lastName?.toUpperCase())
}
输出:
PRAVEEN
7
null
如果值不为空,我们可以将安全调用运算符与 let()、also() 和 run() 一起使用——
let() 方法——
要仅在引用包含不可为空的值时执行操作,我们可以使用 let运算符。仅当变量 firstName 不为 null 时,才会执行 let 中存在的 lambda 表达式。
val firstName: String? = null
firstName?.let { println(it.toUpperCase()) }
这里,变量 firstName 为 null,因此不执行 lambda 表达式将字符串转换为大写字母。
使用 let 的 Kotlin 程序——
科特林
fun main(args: Array) {
// created a list contains names
var stringlist: List = listOf("Geeks","for", null, "Geeks")
// created new list
var newlist = listOf()
for (item in stringlist) {
// executes only for non-nullable values
item?.let { newlist = newlist.plus(it) }
}
// to print the elements stored in newlist
for(items in newlist){
println(items)
}
}
输出:
Geeks
for
Geeks
带有 let() 的also() 方法链 –
如果我们想应用一些额外的操作,比如打印列表中不可为空的项目,我们可以使用also()方法并将其与let()或run()链接起来:
科特林
fun main(args: Array) {
// created a list contains names
var stringlist: List = listOf("Geeks","for", null, "Geeks")
// created new list
var newlist = listOf()
for (item in stringlist) {
// executes only for non-nullable values
item?.let { newlist = newlist.plus(it) }
item?.also{it -> println(it)}
}
}
输出:
Geeks
for
Geeks
run() 方法——
Kotlin 有一个run()方法来对可为空的引用执行一些操作。它似乎与let()非常相似,但在函数体内部,run() 方法仅在我们使用此引用而不是函数参数时才起作用:
科特林
fun main(args: Array) {
// created a list contains names
var stringlist: List = listOf("Geeks","for", null, "Geeks")
// created new list
var newlist = listOf()
for (item in stringlist) {
// executes only for non-nullable values
item?.run { newlist = newlist.plus(this) } // this reference
item?.also{it -> println(it)}
}
}
输出:
Geeks
for
Geeks
猫王操作员(?:) –
Elvis运算符用于在原始变量为空时返回非空值或默认值。换句话说,如果左表达式不为空,则 elvis运算符返回它,否则返回右表达式。仅当发现左侧为空时,才计算右侧表达式。
以下表达式:
val name = firstName ?: "Unknown"
相当于:
val name = if(firstName!= null)
firstName
else
"Unknown"
此外,我们还可以在 Elvis运算符的右侧使用throw和return表达式,这在函数中非常有用。因此,我们可以抛出异常,而不是在 Elvis 运算符的右侧返回默认值。
val name = firstName ?: throw IllegalArgumentException("Enter valid name")
Kotlin 程序使用 Elvis运算符——
科特林
fun main(args: Array) {
var str : String? = "GeeksforGeeks"
println(str?.length)
str = null
println(str?.length ?: "-1")
}
输出:
13
-1
非空断言:!!操作员
not null 断言 (!!)运算符将任何值转换为非 null 类型,如果值为 null,则引发异常。
如果有人想要 NullPointerException,那么他可以使用这个运算符明确地询问。
科特林
fun main(args: Array) {
var str : String? = "GeeksforGeeks"
println(str!!.length)
str = null
str!!.length
}
输出:
13
Exception in thread "main" kotlin.KotlinNullPointerException
at FirstappKt.main(firstapp.kt:8)