在 Kotlin 中学习泛型时,我在一本书中读到以下内容:
In general, a class or interface generic type may be prefixed with out if the class has functions that use it as a return type, or if the class has val properties of that type. You can’t, however, use out if the class has function parameters or var properties of that generic type.
我理解规则所说的内容,但我很乐意(通过示例)理解没有此规则的情况(即在声明通用类/接口(interface)时使用 和 时没有约束),并且还有为什么返回类型可以来自类型 T 并且仍然类/接口(interface)可以包含 不是“危险的”出 T。
无法理解类属性将表现为协变的问题的示例:
class Pet{....}
class Dog:Pet{...}
class PetSomething <T : Pet>
{
T t;
public fun petDoSomething(T t)
{
.... // what can be the problem here?
}
}
class DogSomething
{
dogDoSomething()
{
d : Dog = Dog()
petDoSomething(d)
//what is the problem here???
}
}
此外,本书还显示以下代码:
abstract class E<out T> (t:T) { val x = t }
代码是正在编译,尽管泛型类型是构造函数的输入 .这不违反规则吗?
最佳答案
你引用了:“但是,如果类具有该泛型类型的 函数参数或 var 属性 ,则不能使用 out。”
构造函数不是成员函数或属性,因此不受此规则的约束。在构造函数的位置使用类型作为参数是安全的,因为在构造它时类型是已知的。
考虑这些类:
abstract class Pet
class Cat: Pet()
class Dog: Pet()
class PetOwner<out T: Pet>(val pet: T)
当您调用 PetOwner 构造函数并传入 Cat
,编译器知道你正在构造一个 PetOwner<out Cat>
因为它知道传递给构造函数的值满足 <out Cat>
的类型.它不必向上转换Cat
至Pet
在构造对象之前。然后构造的对象可以安全地向上转换为 PetOwner<Pet>
因为没有 T
将再次传递给实例。不会发生任何不安全的事情,因为没有对参数进行强制转换。函数参数和
var
属性对于 out
是不安全的类型,因为该对象已经构造并且可能已传递给某个变量,该变量已经将其向上转换为其他内容。想象一下编译器让你定义
out T
对于 var
像这样的属性(property):class PetOwner<out T: Pet>(var pet: T)
然后你可以这样做:val catOwner: PetOwner<out Cat> = PetOwner(Cat())
val petOwner: PetOwner<out Pet> = catOwner
petOwner.pet = Dog()
val cat: Cat = catOwner.pet // ClassCastException!
类型安全规则阻止了这种情况的发生。但这对于 val
是不可能的构造函数参数。在将参数传递给构造函数和拥有可以传递的实例之间,无法将对象传递给其他变量并向上转换其类型。
关于java - 如果类/接口(interface)具有 val 属性或具有泛型类型的函数,为什么类/接口(interface)不能以 out 作为前缀?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64413654/