在一种情况下,将Kotlin泛型约束为非null

时间:2018-01-12 23:18:04

标签: generics kotlin

我有一个泛型类型(T: Any?),我需要在一种情况下约束null

class Repository<T> { // T may be a non-null or nullable type.
  fun getFromFoo(): T {}
  fun getFromBar(): T {} // This is never null. Can I mark it as so?
}


val repository = Repository<String>()
val fromFoo: String = repository.getFromFoo() // Returns a non-null String.
val fromBar: String = repository.getFromBar() // How do I make this work?


val repository = Repository<String?>()
val fromFoo: String? = repository.getFromFoo() // Returns a nullable String.
val fromBar: String = repository.getFromBar() // How do I make this return a non-null String?

虽然我理想地将这些重构为单独的类型(例如FooRepositoryBarRepository),但有没有办法获得此类型约束功能?

5 个答案:

答案 0 :(得分:3)

您需要的是T & Any类型 - 通用TAny的交集。它表示T的这种子类型,它永远不能包含空值。

不幸的是,交集类型和此交集特别是当前在Kotlin中是不可表示的,这意味着您不能将函数的返回类型声明为T的非可空子类型。

答案 1 :(得分:2)

反其道而行之。假设您的泛型类型不可为空:

class Repository<T> {
  fun getFromFoo(): T? { ... }
  fun getFromBar(): T { ... }
}

val repository = Repository<String>()
val fromFoo: String? = repository.getFromFoo()
val fromBar: String = repository.getFromBar()

答案 2 :(得分:2)

class Repository<T : Any> { // T may only be a non-null type.
  fun getFromFoo(): T? {} // This may return null.
  fun getFromBar(): T {} // This is never null.
}

val repository = Repository<String>()
val fromFoo: String? = repository.getFromFoo()
val fromBar: String = repository.getFromBar()

需要非null类型。您可以通过T? vs T为您的方法/属性选择性地指明可为空性。

编辑:根据评论中的问题澄清修订解决方案

我现在的印象是,您希望一个方法始终返回非空值,即使T是可空类型。简短的回答是否定的,没有办法做到这一点。

但是,您可以定义两个泛型:

 class Repository<T, N : T> {
  fun getFromFoo(): T {}
  fun getFromBar(): N {}
}

val repository = Repository<String?, String>()
val fromFoo: String? = repository.getFromFoo()
val fromBar: String = repository.getFromBar()

<T, N : T>是一种选择,但它并不强制N属于非可空类型。 <T, N : Any>是另一种选择,这会强制N为非可空类型,但不再强制扩展T。选择你的毒药。

这使得构建存储库变得更加丑陋,但让您在所有呼叫站点都非常无效。

答案 3 :(得分:1)

您不应该使用可空类型进行参数化。您应该使用非null类型,然后在真正需要的地方明确可空性:

class Repository<out T : Any> {
    fun getFromFoo(): T? {
        TODO()
    }

    fun getFromBar(): T {
        TODO()
    }
}

你说T应该是非空类型(T: Any),并说getFromFoo()可能会返回T?,这完全正常。< / p>

答案 4 :(得分:0)

重构为两个单独的类是一个更好的主意,但因为你不希望你可以使用两种泛型类型:

class Repository<T, S: Any> {
    fun getFromFoo(): T? { /* ... */ }
    fun getFromBar(): S { /* ... */ }
}