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

时间:2015-10-08 16:58:12

标签: kotlin

我正在尝试使用泛型类型的lateinit非可空属性创建参数化类:

class Test<T> {

   private lateinit var t : T

   private lateinit var s : String

}

后者是允许的,但前者不是。编译器返回以下错误:

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

由于我没有宣布T?,我很困惑为何会出现这种情况。

4 个答案:

答案 0 :(得分:65)

“默认上限(如果没有指定)是Any?”(http://kotlinlang.org/docs/reference/generics.html#generic-constraints

换句话说,当你使用T时,Kotlin认为这可能是任何类型,无论是原始,对象还是可以为空的引用。

TL; DR;

修复添加上层类型:

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

答案 1 :(得分:1)

Kotlin In Action以下是关于类型参数的可空性的说法

类型参数的可空性:

默认情况下,所有类型参数 Kotlin中的函数和类可以为空。任何类型,包括 可为null的类型,可以代替类型参数;在这种情况下, 使用type参数作为类型的声明被允许 即使类型参数T不以问题结尾 标记。

如何使类型参数不为空?

要使类型参数为非空,您需要指定一个非空 上限。这将拒绝可为空的值作为参数。

请注意,类型参数是以下规则的唯一例外: 最后需要问号将类型标记为可为空,并且 没有问号的类型为非null。下一节显示 可空性的另一种特殊情况:来自Java的类型 代码。

答案 2 :(得分:1)

可空类型参数

Any? 是 Kotlin 中all 类型的超类型。因此,当您没有为类型参数 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 中所有非空类型的超类型。因此,要使泛型类只接受非空类型参数,您需要明确指定 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 必须始终为非空,因为它用于您希望变量为非空但不想在创建对象时初始化其值的情况。

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

要实现这一点,请明确指定上限 T : Any

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

值得注意的是,您可以使用更具体的类型,如果您有一个取决于您的业务逻辑。例如,您可以使用 T : Any 代替 T : SomeProduct,如果这是您想要的上限。它只需要非空。

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


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

答案 3 :(得分:0)

当用作类型参数时,

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