在很早的时候,小黑屋就介绍过如何研究Kotlin,其中涉及到了查看字节码和反编译成Java代码的方式,相信很多人研究过的人,都会或多或少遇到过Intrinsics.checkParameterIsNotNull
这样或者类似的代码。
首先,我们先看一下这段简单的方法
fun dumpStringMessage(message: String) { |
按照我们之前的方法,反编译成Java代码就是这样的
public static final void dumpStringMessage(@NotNull String message) { |
反编译后,我们可以看到代码中有这样的一行代码Intrinsics.checkParameterIsNotNull(message, "message");
Intrinsics 是什么
- Intrinsics是Kotlin内部的一个类
- 包含了检查参数是否为null的
checkParameterIsNotNull
- 包含了表达式结果是否为null的
checkExpressionValueIsNotNull
- 包含了检测lateinit是否初始化的
throwUninitializedPropertyAccessException
- 包含了开发者强制非空!!出现空指针时抛出
throwNpe
的方法 - 判断对象相等的方法
areEqual
- 其他的一些处理数据异常的方法和辅助方法
所以上面代码中的Intrinsics.checkParameterIsNotNull(message, "message");
是为了检测参数message是否为null进行的判断。
为什么会有Intrinsics等判断代码呢
不是说 Kotlin 是空指针安全,有可空(Any?)和不可空(Any)的类型么,我上面的代码声明的是message: String
又不是message: String?
,为什么还要多此一举呢?
是的,你的这句话基本上没有毛病,但是有一个前提,那就是空指针和两种类型的特性,目前只在纯kotlin中生效,一旦涉及到和Java交互时,就不灵了。
比如我们在Java代码中这样调用,不会产生任何编译的问题。
public class JavaTest { |
但是当我们运行时,就会报出这样的错误
Exception in thread "main" java.lang.IllegalArgumentException: Parameter specified as non-null is null: method StringExtKt.dumpStringMessage, parameter message |
所以考虑到方法被Java调用的情况,Kotlin会默认的增加checkParameterIsNotNull
校验。
Intrinsics.checkParameterIsNotNull 一直都有么?
不过好在Kotlin编译器还是足够聪明的,对于不能被Java直接调用的方法,就不会增加相关处理。
比如标记为private的方法,通常情况下,不会被java调用。
private fun innerDumpStringMessage(message: String) { |
反编译成的如下代码,就没有Intrinsics.checkParameterIsNotNull
private static final void innerDumpStringMessage(String message) { |
Intrinsics.checkParameterIsNotNull 的好处
定位排查问题快捷
上面代码的好处之一就是对于代码混淆之后,可以相对更加方便的定位问题。
比如这段代码,经过混淆之后,运行
public class JavaMethod { |
得到如下的崩溃日志
E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.droidyue.intrinsicsmattersandroidsample/com.droidyue.intrinsicsmattersandroidsample.MainActivity}: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method a.a.a.a.a, parameter message |
这里我们可以清晰的看到出问题的参数名称,定位出问题的位置。
其他好处
- 对于先决条件(参数和状态)提前判断可以避免很多不必要的资源消耗。
- 避免不必要的状态产生
Intrinsics的问题
刚才我们提到了Intrinsics可以辅助混淆情况下定位排查问题,但是同时也带来了一个问题,那就是
- 为混淆之后逆向工程提供了更多的帮助。
除此之外,还有人担心Intrinsics是不是存在这样的问题
- Intrinsics调用和返回带来进栈出栈操作,而Intrinsics为java实现,无法在编译时inline,会不会有性能问题
对于性能的担忧可以说是有些过于杞人忧天了,不过还在好在Kotlin提供了方法来消除这种不必要的过虑。当然也能解决逆向混淆的问题。
编译时去除Intrinsics检查
-Xno-param-assertions Don't generate not-null assertions on parameters of methods accessible from Java |
具体的实施方法,可以参考另一篇文章为 Kotlin 项目设置编译选项
其他Intrinsics出现的场景
checkExpressionValueIsNotNull
当Kotlin 调用 Java 获取表达式结果后需要进行操作时,会增加Intrinsics.checkExpressionValueIsNotNull
校验
//Intrinsics.checkExpressionValueIsNotNull(var10000, "JavaUtil.getBook()"); |
Intrinsics.throwNpe
当使用!!
非空断言时,会有校验非空断言结果的检查,如果有问题,则抛出NPE.
/** |
throwUninitializedPropertyAccessException
当尝试访问一个lateinit的属性时,会增加是否初始化的判断,如果有问题,会抛出异常。
class Movie { |
以上就是关于Kotlin编译与 Intrinsics 检查的内容。Enjoy.
相关文章推荐阅读
- 为 Kotlin 项目设置编译选项
- 一个查找字节码更好研究Kotlin的脚本
- 研究学习Kotlin的一些方法
- 其他Kotlin优质文章