Kotlin类型系统摘要

可空性

可控性主要的内容是几个云算法的运用:??.?:as?let!!lateinit这几个运算符或者关键字的使用。值得一提的是,有一种让你惊讶的情况:即使不用问号结尾,类型参数也能是可空的。

fun  printHashCode(t: T) {
    // 因为"t"可能为 null,所以必须使用安全调用
    println(t?.hashCode())
}

// "T"被推导成"Any?"
>>> printHashCode(null)
null

printHashCode调用中,类型参数T推导出的类型是可空类型Any?。因此,尽管没有用问号结尾,实参t依然允许持有null

要使类型参数非空,你必须要为它指定一个非空的上界。那样泛型会拒绝可空值作为实参。

// 现在"T"就不是可空的
fun  printHashCode(t: T) {
    println(t.hashCode())
}
# 这段代码是无法编译的:你不能传递 null,因为期望的是非空值
>>> printHashCode(null)
Error: Type parameter bound for `T` is not satisfied
>>> printHashCode(42)
42
可空性和 Java

有些时候 Java 代码包含了可空性的信息,这些信息使用注解来表达。Java 中的@Nullable String被 Kotlin 当作String?,而@NotNull String就是String

Kotlin 可以识别多种不同风格的可空性注解,包括 JSR-305 标准的注解(在javax.annotation包之中),Android 的注解(android.support.annotation),和 JetBrains 工具支持的注解(org.jetbrains.annotations)。这里有一个有意思的问题,如果这些注解不存在会发生什么?这种情况下,Java 类型会变成 Kotlin 中的平台类型

平台类型本质上就是 Kotlin 不知道可空性信息的类型。你既可以把它当作可空类型处理,也可以当作非空类型处理。这意味着,你要像在 Java 中一样,对你在这个类型上做的操作负有全部责任。编译器将会允许所有的操作,它不会把对这些值的空安全操作高亮成多余的。

在 Kotlin 中你不能声明一个平台类型的变量,这些类型只能来自 Java 代码。但你可能会在 IDE 的错误消息里见到它们:

>>> val i: Int = person.name
ERROR: Type mismatch: inferred type is String! but Int was expected

String!表示法被 Kotlin 编译器用来表示来自 Java 代码的平台类型。你不能在自己的代码里使用这种语法。而且感叹号通常与问题的来源无关,所以通常可以忽略它。它只是强调类型的可空性是未知的。

继承Java类型

当在 Kotlin 中重写 Java 的方法时,你可以选择把参数和返回类型定义成可空的,也可以选择把它们定义成非空的。

/*  Java  */
interface StringProcessor {
    void process(String value);
}
class StringPrinter : StringProcessor {
    override fun process(value: String) {
        println(value)
    }
}

class NullableStringPrinter : StringProcessor {
    override fun process(value: String?) {
        if (value != null) {
            println(value)
        }
    }
}

注意在实现 Java 类或者接口的方法时一定要搞清楚它的可空性。因为方法的实现可以在非 Kotlin 的代码中被调用,Kotlin 编译器会为你声明的每一个非空的参数生成非空断言。如果 Java 代码传给这个方法一个null值,断言将会触发,你会得到一个异常,即便你从没有在你的实现里访问过这个参数的值。

基本数据类型

Any 和 Any?

Any类型是 Kotlin 所有非空类型的超类型(非空类型的根)。在 Kotlin 中如果你需要可以持有任何可能值的变量,包括null在内,你必须使用Any?类型。

在底层,Any类型对应java.lang.Object。Kotlin 把 Java 方法参数和返回类型中用到的Object类型看作Any。(更确切地是当作平台类型,因为其可空性是未知的)。当 Kotlin 函数使用Any时,它会被编译成 Java 字节码中的Object

Unit 类型:Kotlin 的“void”
fun f(): Unit { ... }
// 显式的 Unit 声明被省略了
fun f(): { ... }

Unit是一个完备的类型,可以作类型参数,而void却不行。只存在一个值是Unit类型,这个值也叫作Unit,并且(在函数中)会被隐式地返回。

Nothing 类型: “这个函数永不返回”
fun fail(message: String): Nothing {
    throw IllegalStateException(message)
}

集合与数组

collection.png
create_collection.png

Kotlin 与 Java 集合是可以相互转换的,至于集合的可控性需要使用时自行判断。

数组

如果你声明了一个Array,它将会是一个包含装箱整型的数组(它的 Java 类型将是java.lang.Integer[])。如果你需要创建没有装箱的基本数据类型的数组,必须使用一个基本数据类型数组的特殊类。Int类型值的数组叫做IntArray。 Kotlin 还提供了ByteArrayCharArrayBooleanArray等等给其他类型。所有这些类型都被编译成普通的 Java 基本数据类型数组,比如int[]byte[]char[]等等。

val fiveZeros = IntArray(5)
val fiveZerosToo = intArrayOf(0, 0, 0, 0, 0)
val squares = IntArray(5) { i -> (i+1) * (i+1) }

你可能感兴趣的:(Kotlin类型系统摘要)