Kotlin-可为空的接收器扩展将不接受非可为空的等效项

时间:2019-01-26 10:35:40

标签: kotlin

考虑以下示例:

class Foo<T>

fun <T> Foo<Iterable<T>?>.bar(i: Iterable<T>) {
    ...
}

Foo有一个扩展方法bar,它需要一个Iterable<T>?的接收者

请考虑以下用例:

val x = listOf(123, 456)
val f = Foo<Iterable<Int>>()

f.bar(x)

f上的编译器错误:

  

类型不匹配

     

必填:Foo ?>

     

发现:Foo >

我可以理解这无法正常工作,试图将可为null的类型传递给不可为空的接收器,但是我不明白为什么不能将不可为null的类型传递给可为空的接收器。

基本上,我想说的是“接收者可能为空,但是在这种情况下,我可以保证它不是”

关于如何解决此问题的任何想法,因此扩展方法将同时允许可空和不可空类型?

注意:我无法将val f = Foo<Iterable<Int>>更改为val f = Foo<Iterable<Int>?>,因为这是根据属性的类型自动确定的。

3 个答案:

答案 0 :(得分:3)

只需添加out修饰符,它将起作用:

class Foo<out T>

我们使用out修饰符表示协方差(类似于Java中的“?extended T”)。 协方差-是将通用类型参数从类更改为其父级之一的功能,即将List<String>分配给List<Any>

以下是有关generics的文档。

答案 1 :(得分:3)

替代@Sergey的答案,您可以使用use-site variance而不是declaration-site variance(因为class Foo<out T>会限制T的使用并影响所有用法)并添加{扩展程序声明中的{1}}修饰符:

out

(runnable sample)

fun <T> Foo<out Iterable<T>?>.bar(i: Iterable<T>) { /* ... */ } 修饰符意味着扩展名不仅将完全接受out,而且还将接受任何类型为Foo<Iterable<T>?>的可空Iterable的子类型,例如{{1} },T,并且由于非null类型被视为可为null的子类型,因此扩展名将接受List<T>?类型的非null对应。

答案 2 :(得分:1)

您可以达到Hotkey和Sergey这样描述的相同效果:

fun <T, S: Iterable<T>?> Foo<S>.bar(i: Iterable<T>) { /* ... */ }

它是多余的,但可能更容易理解。由于您将Iterable<T>?定义为S的上限,因此现在也允许包括Iterable<T>的任何子类型。

理解了这个概念之后,我仍然会选择out Iterable<T>?,因为它更简洁。