java 类映射_Java 互操作:平台类型与类型映射

Kotlin 号称 与 Java 100% 兼容,拥有十分良好的 Java 互操作性。我们在之前的学习中一直都涉及到 Kotlin 代码与 Java 代码的互通问题,所以 Java 互操作这一部分的内容不会很多,主要讲一些之前没有涉及到的东西。

通过 Kotlin 反射机制获取对象的类型

为了说明平台类型,我们简单介绍一下如何通过 Kotlin 的反射机制获取对象的类型。Kotlin 反射功能并不包含在我们之前用的 kotlin-runtime.jar 内,而是在 kotlin-reflect.jar 内。如果 IDE 没有自动添加 kotlin.reflect.jar 到外部库内,可以通过 Maven 或 Gradle 将与 kotlin-runtime.jar 版本相对应的 kotlin.reflect.jar 添加到依赖里,具体方法各位小伙伴可以去找找相应的教程。

在 Kotlin 里,要获取一个对象的类型,有两种方法:

val s = "Hello World"

println(s::class)

println(s::class.java)

println(s.javaClass)对象::class 得到一个 kotlin.reflect.KClass 对象,它是这个对象在 Kotlin 中的类型;

对象::class.java 和 对象.javaClass 得到一个 java.lang.Class 对象,它是这个对象在 JVM 中加载的类型。

上面打印的结果如下:

class kotlin.String

class java.lang.String

class java.lang.String

同一个对象在 Kotlin 和 JVM 系统中表现为不同的类型,对于 Kotlin 代码来说,这个对象就是 kotlin.String 类型,而对于 Java 代码来说,它就是 java.lang.String 类型。

平台类型

Kotlin 解决 Java 的空安全问题,最关键的一点就是区分开了非空类型和可空类型。这点在“Kotlin 代码调用 Kotlin 代码”和“Java 代码调用 Kotlin 代码”时完全没有问题,但在“Kotlin 代码调用 Java 代码”时就会出现问题。因为 Java 所有引用类型都可以是 null,这在我们通过 Kotlin 调用 Java 代码时就可能出现问题:

// import java.io.Fileval file: File = File("C:\\FakeFile")

println(file::class)

val files: Array = file.listFiles()

println(files::class)

我们这里用一个并不存在的 "C:\\FakeFile" 文件来新建一个 File 对象 file,因为是用构造函数创建的对象,我们可以确认 file 对象不是 null,因此 file::class 一定会打印出 "class java.io.File"。

但第三行就出问题了,因为 file 对象表示的文件并不存在,file.listFiles() 会返回一个 null,我们在试图把 null 赋给非空的 Array 对象 files 时,就会出现 IllegalStateException:

java.lang.IllegalStateException: file.listFiles() must not be null

关键在于,这个 null 导致的问题在编译时根本不会被发现,而是在运行时抛出了异常,无异于破坏了 Kotlin 的空安全。

Kotlin 对这种问题的解决方法是引入了 平台类型 的概念:所有 Java 引用类型在 Kotlin 中都表现为平台类型,这些 Java 类型的空安全在 Kotlin 中的空安全检查会被放宽,在与 Java 类型交互时,一定要考虑到 null 的问题。上面的代码应该这样写:

val files: Array? = file.listFiles()

files?.let { println(it::class) }

与此同时 ,Kotlin 还引入了 ! 来表示平台类型,这个 ! 标识符我们在写 Kotlin 代码时不会用到,它只用来表示 Java API(Intellij IDEA 中,可以按住 Ctrl 键把光标移动到方法、变量、类型等上面显示签名),比如上面的 listFiles() 方法在 Kotlin 中就表现为:

open fun listFiles(): Array!

如果我们不显式指定 files 的类型,Kotlin 就自动推断为 Array!,它表示下面几种情况:File!:数组内的 File 可能是 null,也可能不是 null;

:(out) 表示数组可能是协变的,也可能不协变;

Array!:这个数组本身可能是 null,也可能不是 null;

因此,这个方法的返回值在 Kotlin 中 最安全 的 显式表示 是:Array?。但我们知道,listFiles() 方法只会返回 File[],不可能包含 null 对象,File 类也没有子类,所以我们可以再限制一下条件,把 files 对象声明为 Array?,这也是安全的。

类型映射

如果所有 Java 类型都使用平台类型表示,Kotlin 的空安全就没有意义了。Kotlin 对于Java 基本数据类型和一些常用的引用类型,都会直接映射为 Kotlin 中已经定义好了的类型,这就是类型映射。

比如常用的集合实现类,Kotlin 都已经定义好的类型映射。比如 ArrayList:

// kotlin.collections.TypeAliases.ktpackage kotlin.collections

@SinceKotlin("1.1") public typealias ArrayList = java.util.ArrayList

Kotlin 中使用 typealias 关键字定义类型映射,这里就定义了一个 kotlin.collections.ArrayList 与 java.util.ArrayList 的类型映射。

这意味着,所有在 Kotlin 中不加标注使用的 ArrayList 都是 kotlin.collections.ArrayList ,但这个类型并不真实存在,编译时会被转换为 java.util.ArrayList,通过 Java 代码调用 kotlin.collections.ArrayList 对象,会被自动转换为 java.util.ArrayList;而且所有 Java 中的 java.util.ArrayList 在 Kotlin 代码中都可以通过 kotlin.collections.ArrayList 调用。

目前 Kotlin 已经映射的类型有如下几类:Java 基本类型 Kotlin 基本类型:如 Java 中的 int 类型映射为 http://Kotlin.Int 类型;

Java 基本类型包装类 Kotlin 可空基本类型:如 java.lang.Integer 类型映射为 kotlin.Int? 类型;

Java 基本类型数组 Kotlin 基本类型数组:如 Java 的 int[] 映射为 kotlin.IntArray! 类型;

Java 引用类型数组 Kotlin 数组:如 Java 的 String[] 映射为 kotlin.Array!

其他类型映射:Java 中的 Object、Cloneable、Comparable、Enum、Annotation、Deprecated、CharSequence、String、Number、Throwable 映射为 Kotlin 中对应类型的平台类型。如 java.lang.String 类型映射为 kotlin.String! 类型。

这里有几个细节:泛型参数一定映射为平台类型,比如 Java 的 ArrayList 映射为 Kotlin 中的 ArrayList!。

Kotlin 把集合接口区分为可变和不可变两种,而 Java 中没有这个限制,因此 Java 中的 List 映射为 Kotlin 中的 (Mutable)List! 类型,它表示这个 List 可能可变,也可能不可变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值