Kotlin - 可为空的接收器扩展不接受不可为空的等效项
Kotlin - nullable receiver extension won't accept non-nullable equivalent
考虑以下示例:
class Foo<T>
fun <T> Foo<Iterable<T>?>.bar(i: Iterable<T>) {
...
}
Class Foo
有一个扩展方法,bar
需要 Iterable<T>?
的接收者
考虑以下用例:
val x = listOf(123, 456)
val f = Foo<Iterable<Int>>()
f.bar(x)
f
上的编译器错误:
Type mismatch
Required: Foo<Iterable<Int>?>
Found: Foo<Iterable<Int>>
我可以理解这不是反过来工作,试图将可空类型传递给不可空接收者,但我不明白为什么我不能将不可空类型传递给可空接收者。
基本上我想说的是“接收者可能为空,但在这种情况下,我可以保证它不是”
有没有想过如何解决这个问题,以便扩展方法同时允许可空和不可空类型?
注:我无法将val f = Foo<Iterable<Int>>
改为val f = Foo<Iterable<Int>?>
,因为这是根据属性的类型自动决定的。
只需添加 out
修饰符即可:
class Foo<out T>
我们使用out
修饰符来表示协方差(类似于Java中的“?extends T”)。 Covariance - 是将泛型类型参数从 class 更改为其父项之一的能力,即将 List<String>
分配给 List<Any>
。
这是关于 generics 的文档。
替代@Sergey 的回答,您可以使用 use-site variance rather than declaration-site variance(因为 class Foo<out T>
将限制 T
的使用并影响所有用法)并添加 out
修饰符在扩展声明处:
fun <T> Foo<out Iterable<T>?>.bar(i: Iterable<T>) { /* ... */ }
out
修饰符意味着扩展将不仅接受完全 Foo<Iterable<T>?>
,还会接受使用 T
键入的可空 Iterable
的任何子类型,例如 List<T>?
、Set<T>?
,而且,由于非空类型被视为可空类型的子类型,扩展将接受 Iterable
类型的非空对应项。
你可以达到 Hotkey 和 Sergey 描述的相同效果:
fun <T, S: Iterable<T>?> Foo<S>.bar(i: Iterable<T>) { /* ... */ }
它比较冗余,但可能更容易理解。由于您将 Iterable<T>?
定义为 S
的上限,因此现在也允许包含 Iterable<T>
的任何子类型。
掌握概念后,我还是会选择out Iterable<T>?
,因为它更简洁。
考虑以下示例:
class Foo<T>
fun <T> Foo<Iterable<T>?>.bar(i: Iterable<T>) {
...
}
Class Foo
有一个扩展方法,bar
需要 Iterable<T>?
考虑以下用例:
val x = listOf(123, 456)
val f = Foo<Iterable<Int>>()
f.bar(x)
f
上的编译器错误:
Type mismatch
Required: Foo<Iterable<Int>?>
Found: Foo<Iterable<Int>>
我可以理解这不是反过来工作,试图将可空类型传递给不可空接收者,但我不明白为什么我不能将不可空类型传递给可空接收者。
基本上我想说的是“接收者可能为空,但在这种情况下,我可以保证它不是”
有没有想过如何解决这个问题,以便扩展方法同时允许可空和不可空类型?
注:我无法将val f = Foo<Iterable<Int>>
改为val f = Foo<Iterable<Int>?>
,因为这是根据属性的类型自动决定的。
只需添加 out
修饰符即可:
class Foo<out T>
我们使用out
修饰符来表示协方差(类似于Java中的“?extends T”)。 Covariance - 是将泛型类型参数从 class 更改为其父项之一的能力,即将 List<String>
分配给 List<Any>
。
这是关于 generics 的文档。
替代@Sergey 的回答,您可以使用 use-site variance rather than declaration-site variance(因为 class Foo<out T>
将限制 T
的使用并影响所有用法)并添加 out
修饰符在扩展声明处:
fun <T> Foo<out Iterable<T>?>.bar(i: Iterable<T>) { /* ... */ }
out
修饰符意味着扩展将不仅接受完全 Foo<Iterable<T>?>
,还会接受使用 T
键入的可空 Iterable
的任何子类型,例如 List<T>?
、Set<T>?
,而且,由于非空类型被视为可空类型的子类型,扩展将接受 Iterable
类型的非空对应项。
你可以达到 Hotkey 和 Sergey 描述的相同效果:
fun <T, S: Iterable<T>?> Foo<S>.bar(i: Iterable<T>) { /* ... */ }
它比较冗余,但可能更容易理解。由于您将 Iterable<T>?
定义为 S
的上限,因此现在也允许包含 Iterable<T>
的任何子类型。
掌握概念后,我还是会选择out Iterable<T>?
,因为它更简洁。