Kotlin类型系统总结(可空类型、非空断言、类型检查、智能转换、强制转换)

Java如何解决NPE问题?

1.函数内对于无效值,更倾向于抛异常处理。特别地,在Java里应该使用专门的自定义Checked Exception。对于经常出现无效值的、有性能需求或在代码中经常使用的函数并不合适。对于自身可取空值的类型,比如说集合类型,通常返回零长度的数组或者集合,虽然会多出内存开销。

2.采用@NotNull/@Nullable标注。对于一段复杂的代码,检查参数是否为空是一件比较耗时的事情。对于不可为空的参数,可以使用 @NotNull来注解,明确参数是否为空,在模块入口加以控制,避免非法null进一步传递。

3.使用专门的Optional对象对可能为null的变量进行装箱。这类Optional对象必须拆箱后才能参与运算,因而拆箱步骤就是提醒使用者必须处理null值的情况。

Kotlin中区分非空(non-null)和可空(nullable)类型。默认是非空类型,通过加上?表示可空类型。

由于null只能被存储在Java的引用类型的变量中,所以在Kotlin中基本数据的可空版本都会使用该类型的包装类型。同样如果使用基本数据类型作为泛型类的类型参数,Kotlin同样使用该类型的包装形式。

例:

data class Glasses(val degreeOfMyopia: Double)
data class Student(val glasses: Glasses?)
data class Seat(val student: Student?)

fun main() {
    val glasses = Glasses(100.0)
    val student = Student(glasses)
    val seat = Seat(student)

    println("该位置上学生眼镜的度数:${seat.student?.glasses?.degreeOfMyopia}")
}

该位置上学生眼镜的度数:100.0

Elvis操作符 ?:

Kotlin中与三目运算符相似的用法

val result = seat.student?.glasses?.degreeOfMyopia ?: -1

非空断言 !!

println(seat1.student!!.glasses!!.degreeOfMyopia)

Kotlin可空类型实现

Kotlin

fun payPrint(str: String?) {
    println(str?.length)
}

转Java

public static final void payPrint(@Nullable String str) {
   Integer var1 = str != null ? str.length() : null;
   System.out.println(var1);
}

通过参数上标注@Nullable。这样做的原因:

1.兼容Java老版本(兼容Android)。

2.实现Java与Kotlin的100%转换。

3.性能上达到最佳。

Kotlin中抛出异常

val student1 = Student(null)
val seat1 = Seat(student1)
val result1 = seat1.student?.glasses?.degreeOfMyopia ?:throw NullPointerException("some reasons")

let的概念

调用某对象的let函数,该对象会作为函数的参数,在函数块内可以通过it指代该对象。返回值为函数块的最后一行或指定return表达式。

/**
 * Calls the specified function [block] with `this` value as its argument and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

类型检查

在Java中使用A instanceof T 来判断A是T或者T的子类的一个实例。而在Kotlin中,使用is关键字来判断。

fun main() {
    val str = "Pay"

    when (str) {
        is String -> println(str.length)
        !is String -> println("Not a String")
    }
}

3

类型智能转换

Smart Casts可以将一个变量的类型转变为另一种类型,它是隐式完成的。其实是Kotlin编译器帮助我们做出了转换。

当且仅当Kotlin的编译器确定在类型检查后该变量不会再发生改变,才会产生Smart Casts。

val stu: Any = Student(Glasses(666.0))
if (stu is Student) {
    println(stu.glasses?.degreeOfMyopia)
}

 对比下面两段代码(一个用val修饰,一个用var修饰)

data class PayGlasses(val degreeOfMyopia: Double)
data class PayStudent(val glasses: PayGlasses?)
data class PaySeat(val student: PayStudent?)

class PayKot {
    //正确,编译器不会报错
    val stu: PayStudent? = PayStudent(PayGlasses(999.0))
    fun dealStu() {
        if (stu != null) {
            println(stu.glasses)
        }
    }
}

class PayKot {
    var stu: PayStudent? = PayStudent(PayGlasses(999.0))    
    fun dealStu() {
        if (stu != null) {
            //错误,编译器会报错
            println(stu.glasses)
        }
    }
}

//也可以使用let来修改
class PayKot {
    var stu: PayStudent? = PayStudent(PayGlasses(999.0))
    fun dealStu() {
        stu?.let { 
            println(it.glasses)
        }
    }
}

在上面的代码中,用val修饰的stu是可以保证线程安全的,所以编译器不会报错。

用var修饰的stu,意味着在判断(stu != null)之后,stu在其他线程中还是被修改的,所以编译器会报错。

通过let函数也可以解决这个问题。

Kotlin中使用as关键字来实现强制转换。

关于as关键字的使用注意点:

下面这种写法是不安全的,如果getStudent()返回null,则会导致类型转换异常。

//可能产生类型转换异常
val stu: PayStudent? = getStudent() as PayStudent

通过as?实现安全的转换。此时如果stu为空,不会抛出异常,而是会返回null。

val stu: PayStudent? = getStudent() as? PayStudent

可以配合泛型封装一个类型转换的方法

//目标是将任意不为空的类型转换为目标类型T,因为可能转换失败,则返回类型为T?
fun <T> cast(original: Any): T? = original as? T

但还是会有一个问题,观察下面代码(会报错)

java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String

fun <T> cast(original: Any): T? = original as? T

fun main() {
    val ans = cast<String>(123456L)
}

这是类型擦除造成的影响。

解决方案如下:

Kotlin 中使用关键字reified,我们可以理解为“具体化”,利用它,我们可以在方法体内访问泛型指定的JVM对象(注意,需要方法前加上inline修饰)。

inline fun <reified T> cast(original: Any): T? = original as? T

fun main() {
    val ans = cast<String>(123456L)
    println(ans)
}

null

参考Kotlin核心编程

发布了179 篇原创文章 · 获赞 175 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/zhangying1994/article/details/104314972