为什么我不能在Swift中使用泛型类的子类?

Con*_*ode 8 generics subclassing swift

为什么Swift不允许我为Foo<U>类型的变量赋值Foo<T>,其中U是T的子类?

例如:

class Cheese {
    let smell: Int
    let hardness: Int
    let name: String

    init(smell: Int, hardness: Int, name: String) {
        self.smell = smell
        self.hardness = hardness
        self.name = name
    }

    func cut() {
        print("Peeyoo!")
    }
}

class Gouda: Cheese {
    let aged: Bool

    init(smell: Int, hardness: Int, name: String, aged: Bool) {
        self.aged = aged
        super.init(smell: smell, hardness: hardness, name: name)
    }

    override func cut() {
        print("Smells delicious")
    }
}

class Platter<Food> {
    var food: Food

    init(food: Food) {
        self.food = food
    }
}

let goudaCheese = Gouda(smell: 6, hardness: 5, name: "Gouda", aged: false)
let goudaPlatter = Platter(food: goudaCheese)  //Platter<Gouda>

//error: cannot assign value of type 'Platter<Gouda>' to type 'Platter<Cheese>'
let platter: Platter<Cheese> = goudaPlatter
Run Code Online (Sandbox Code Playgroud)

但为什么它不起作用?您可以为变量分配一个对象,该对象是其类型的子类,例如

let gouda = Gouda(smell: 6, hardness: 5, name: "Gouda", aged: false)
let cheese: Cheese = gouda
Run Code Online (Sandbox Code Playgroud)

您可以将子类添加到集合:

let plainCheese = Cheese(smell: 2, hardness: 5, name: "American")
let gouda = Gouda(smell: 6, hardness: 5, name: "Gouda", aged: false)
var cheeses: [Cheese] = [plainCheese]
cheeses.append(gouda)
Run Code Online (Sandbox Code Playgroud)

那有什么let platter: Platter<Cheese> = goudaPlatter不同呢?在任何情况下,如果它起作用会不安全吗?它只是当前Swift版本的限制吗?

Chr*_*Vig 6

您可以使用称为类型擦除的技术解决此问题.基本上,您创建了一个"包装器"结构,它隐藏了泛型中的基础类详细信息.它并不理想,但它可以让你完成类似于你想要做的事情.

class Cheese {
    func doSomethingCheesy() {
        print("I'm cheese")
    }
}

class Gouda: Cheese {
    override func doSomethingCheesy() {
        print("I'm gouda")
    }
}

struct AnyCheese {
    let cheese: Cheese
}

class Container<T> {
    init(object: T) {
        self.object = object
    }
    let object: T
}

let cheese = Cheese()
let cheeseContainer: Container<AnyCheese> = Container(object: AnyCheese(cheese: cheese))

let gouda = Gouda()
let goudaContainer: Container<AnyCheese> = Container(object: AnyCheese(cheese: gouda))

cheeseContainer.object.cheese.doSomethingCheesy() // prints "I'm cheese"
goudaContainer.object.cheese.doSomethingCheesy()  // prints "I'm gouda"
Run Code Online (Sandbox Code Playgroud)

  • 我对你的答案投了赞成票,因为这是对类型擦除的一个很好的解释,我相信它会对发现这个问题的人有所帮助。然而,我没有将其标记为已接受,因为我要求解释为什么 Swift 会这样做,而不是寻找解决方法。我认为对我的问题的评论可以重新修改为一个好的答案。 (2认同)