赞
踩
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核心编程
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。