有没有办法将“Self”限制为通用类型?

Is there a way to constrain `Self` to a generic type?

https://www.raywenderlich.com/148448/introducing-protocol-oriented-programming

protocol Bird {
    var name: String { get }
    var canFly: Bool { get }
    func doSomething()
}

protocol Flyable {
    var airspeedVelocity: Double { get }
}

extension Bird {
    // Flyable birds can fly!
    var canFly: Bool { return self is Flyable }
    func doSomething() {
        print("default Bird: \(name)")
    }
}

class FlappyBird: Bird, Flyable {
    let name: String
    let canFly = true
    var airspeedVelocity: Double = 5.0

    init(name: String) {
        self.name = name
    }
}

class Penguin: Bird {
    let name: String
    let canFly = false

    init(name: String) {
        self.name = name
    }
}

class Owl<T> : Bird {
    let name: String
    let power: T

    init(name: String, power: T) {
        self.name = name
        self.power = power
    }
}

extension Bird where Self: FlappyBird {
    func doSomething() {
        print("FlappyBird: \(name)")
    }
}

extension Bird where Self: Owl<String> {
    func doSomething() {
        print("Owl<String>: \(name)")
    }
}

    let fb = FlappyBird(name:"PAK")
    let penguin = Penguin(name:"Mr. Pickle")
    let nightOwl = Owl<String>(name:"Night Owl", power:"Who")
    let dayOwl = Owl<Int>(name:"Day Owl", power: 50)

    let birds: [Bird] = [fb, penguin, nightOwl, dayOwl]

    birdloop: for bird in birds {
        bird.doSomething()
    }

我得到的输出是:

FlappyBird: PAK
default Bird: Mr. Pickle
default Bird: Night Owl
default Bird: Day Owl

以来第一个结果按预期工作
extension Bird where Self: FlappyBird {
    func doSomething() {
        print("FlappyBird: \(name)")
    }
}

第二个结果按预期工作,因为它调用了默认协议扩展:

extension Bird {
        // Flyable birds can fly!
        var canFly: Bool { return self is Flyable }
        func doSomething() {
            print("default Bird: \(name)")
        }
    }

我希望打印的第三个结果

Owl<String>: Night Owl

因为 nightOwlOwl<String> 类型。但它调用默认协议扩展:

default Bird: Night Owl

有什么原因吗

extension Bird where Self: FlappyBird {
  func doSomething() {
    print("default Bird: \(name)")
  }
}

被称为 FlappyBird 类型,但是

extension Bird where Self: Owl<String> {
  func doSomething() {
    print("Owl<String>: \(name)")
  }
}

不是 Owl<String> 类型吗?

对于泛型类型 Owl<T>,您可以使用约束 where Self: Owl<String>,但它仅适用于具体类型信息可用的上下文。

为了弄清楚发生了什么,请考虑以下内容:

let nightOwl = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "Owl<String>: Night Owl"

与此相反:

let nightOwl: Bird = Owl<String>(name: "Night Owl", power: "Who")
nightOwl.doSomething() // prints "default Bird: Night Owl"

当 Swift 为类型 Owl<T>FlappyBird 创建协议见证 tables 时,它必须对每个类型采取不同的行动,因为 Owl是通用的。如果它没有具体的类型信息,即调用站点的 Owl<String>,它必须使用 Owl<T> 的默认实现。当您将猫头鹰插入 [Bird].

类型的数组时,会出现这种 "loss" 类型信息

FlappyBird 的情况下,由于只有一种可能的实现(因为它不是通用的),编译器生成一个带有 "expected" 方法引用的 witness table,它是 print("FlappyBird: \(name)")。由于 FlappyBird 不是通用的,它的 witness table 不需要对 doSomething() 的无约束默认实现的任何引用,因此即使在具体类型信息是丢失的。

为了明确编译器 "needs" 具有泛型类型的回退行为,您可以从 Owl<T> 中删除 Bird 一致性并尝试仅依赖受约束的默认实现。这将导致编译错误,与 Swift 一样,具有高度误导性的错误。

Value of type 'Owl' has no member 'doSomething'

基本上,似乎无法构建见证 table,因为它需要存在一个适用于 Owl 上所有类型 T 的实现。

参考资料

  1. https://forums.swift.org/t/swifts-method-dispatch/7228
  2. https://developer.apple.com/videos/play/wwdc2016/416/

是对原因部分的恰当解释。

至于修复部分,将通用 OwldoSomething() 实现为:

class Owl<T> : Bird {
    //...
    func doSomething() {
        print("Owl<\(type(of: power))>: \(name)")
    }
}

现在您在 extension Bird where Self: Owl<String>

中不再需要 doSomething()