当前位置:   article > 正文

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

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同样使用该类型的包装形式。

例:

  1. data class Glasses(val degreeOfMyopia: Double)
  2. data class Student(val glasses: Glasses?)
  3. data class Seat(val student: Student?)
  4. fun main() {
  5. val glasses = Glasses(100.0)
  6. val student = Student(glasses)
  7. val seat = Seat(student)
  8. println("该位置上学生眼镜的度数:${seat.student?.glasses?.degreeOfMyopia}")
  9. }
  10. 该位置上学生眼镜的度数:100.0

Elvis操作符 ?:

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

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

非空断言 !!

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

Kotlin可空类型实现

Kotlin

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

转Java

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

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

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

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

3.性能上达到最佳。

 

Kotlin中抛出异常

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

 

let的概念

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

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

 

类型检查

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

  1. fun main() {
  2. val str = "Pay"
  3. when (str) {
  4. is String -> println(str.length)
  5. !is String -> println("Not a String")
  6. }
  7. }
  8. 3

类型智能转换

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

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

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

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

  1. data class PayGlasses(val degreeOfMyopia: Double)
  2. data class PayStudent(val glasses: PayGlasses?)
  3. data class PaySeat(val student: PayStudent?)
  4. class PayKot {
  5. //正确,编译器不会报错
  6. val stu: PayStudent? = PayStudent(PayGlasses(999.0))
  7. fun dealStu() {
  8. if (stu != null) {
  9. println(stu.glasses)
  10. }
  11. }
  12. }
  13. class PayKot {
  14. var stu: PayStudent? = PayStudent(PayGlasses(999.0))
  15. fun dealStu() {
  16. if (stu != null) {
  17. //错误,编译器会报错
  18. println(stu.glasses)
  19. }
  20. }
  21. }
  22. //也可以使用let来修改
  23. class PayKot {
  24. var stu: PayStudent? = PayStudent(PayGlasses(999.0))
  25. fun dealStu() {
  26. stu?.let {
  27. println(it.glasses)
  28. }
  29. }
  30. }

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

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

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

 

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

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

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

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

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

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

 

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

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

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

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

  1. fun <T> cast(original: Any): T? = original as? T
  2. fun main() {
  3. val ans = cast<String>(123456L)
  4. }

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

 

解决方案如下:

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

  1. inline fun <reified T> cast(original: Any): T? = original as? T
  2. fun main() {
  3. val ans = cast<String>(123456L)
  4. println(ans)
  5. }
  6. null

 

参考Kotlin核心编程

 

 

 

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/992342
推荐阅读
相关标签
  

闽ICP备14008679号