为什么泛型类型 属性 可以为空?

Why is a generic typed property nullable?

我正在尝试使用 lateinit 不可为空 属性 的通用类型创建参数化 class:

class Test<T> {

   private lateinit var t : T

   private lateinit var s : String

}

后者是允许的,前者是不允许的。编译returns出现如下错误:

Error:(7, 11) ''lateinit'' modifier is not allowed on nullable properties

因为我没有声明T?,所以我很困惑为什么会这样。

The default upper bound (if none specified) is Any? (Source)

换句话说,当您使用 T 时,Kotlin 假定这可能是 任何 类型,无论是原始类型、对象还是可空引用。

要解决此问题,请添加上部类型:

class Test<T: Any> { ... }

Kotlin In Action 对类型参数的可空性有以下说法

Nullability of type parameters:

By default, all type parameters of functions and classes in Kotlin are nullable. Any type, including a nullable type, can be substituted for a type parameter; in this case, declarations using the type parameter as a type are allowed to be null, even though the type parameter T doesn’t end with a question mark.

如何让类型参数不为空?

To make the type parameter non-null, you need to specify a non-null upper bound for it. That will reject a nullable value as an argument.

Note that type parameters are the only exception to the rule that a question mark at the end is required to mark a type as nullable, and types without a question mark are non-null. The next section shows another special case of nullability: types that come from the Java code.

T,当用作类型参数时,始终为空。 (所有类型参数都可以为空)。您不需要声明 T?,只需要 T。要使用声明上限,请执行此操作 public class Foo<T: Any>Any 不可为空)

可空类型参数

Any? 是 Kotlin 中 所有 类型的超类型。因此,当您没有为类型参数 T 指定任何上限时,默认界限为 Any?.

例如:

class Test<T> { }

相同

class Test<T : Any?> { }

这导致 T 在以下示例中可以为空:

class Test<T> {
    private var t : T          // T can have a nullable type
}

这意味着上面的泛型类型可以用可空和非空类型参数实例化:

val test: Test<Int> = Test()   // OK
val test: Test<Int?> = Test()  // OK

非空类型参数

Any 是 Kotlin 中 所有非空 类型的超类型。因此,要使泛型 class 仅接受非空类型参数,您需要明确指定 Any 作为 T 的上限,即 T : Any.

这导致 T 在以下示例中为非空:

class Test<T : Any> {
    private var t: T           // T is non-null
    private var t2: T?         // T can be used as nullable
}

具有 T : Any 的泛型类型只能使用非空类型参数实例化,并阻止使用可空类型参数实例化:

val test: Test<Int> = Test()   // OK
val test: Test<Int?> = Test()  // Error

lateinit var

lateinit var 必须始终为非空,因为它用于您希望变量为非空但又不想在创建对象时初始化其值的情况。

因此,要创建与类型参数 T 具有相同类型的 lateinit 变量,类型参数也需要为非空。

为此,明确指定上限 T : Any

class Test<T : Any> {
    private lateinit var t: T
}

值得注意的是,您可以根据业务逻辑使用更具体的类型。例如,您可以使用 T : SomeProduct 而不是 T : Any,如果这是您希望的上限。它只需要是非空的。

这将确保您的 class 的用户无法使用可空类型参数进行实例化,并且您对 lateinit var 始终为非空的假设将成立。


就是这样!希望对您有所帮助。