java - 如果类/接口(interface)具有 val 属性或具有泛型类型的函数,为什么类/接口(interface)不能以 out 作为前缀?

标签 java kotlin generics

在 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> 的类型.它不必向上转换CatPet在构造对象之前。然后构造的对象可以安全地向上转换为 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/

相关文章:

java - 在 Java 中使用大写字母在 main 之外定义 Scanner 类型的常量是一个好习惯吗

java - 为什么我们不能以常规方式在外部类的主要方法中创建内部类的实例?

Java swing、SwingWorker、进程栏不会更新

sql - 如何使用 NamedJdbcTemplate 只选择一个字符串值

java - 当使用 Type 而不是 Class<T> 时,有没有办法避免使用泛型进行强制转换

java - 在 JAVA 对象中存储和获取值

android - 由于 Hilt 错误,应用程序未初始化

spring - 如何在 Kotlin 和 Spring 中使用 setter 注入(inject)?

java - JDK 1.7 打破了向后兼容性? (泛型)

linq - 使用在运行时解析的类型调用 System.Linq.Queryable 方法