有没有办法将“Self”约束为泛型类型?

时间:2018-04-13 11:33:50

标签: swift generics protocols extension-methods

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

因为nightOwl属于Owl<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>类型调用

2 个答案:

答案 0 :(得分:1)

对于泛型类型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创建协议见证表时,它必须对每个类型采取不同的行为,因为Owl是通用的。如果它没有具体类型信息,即呼叫站点的Owl<String>,则必须使用Owl<T>的默认实现。这&#34;损失&#34;当您将猫头鹰插入类型为[Bird]的数组中时,会发生类型信息。

FlappyBird的情况下,由于只有一种可能的实现(因为它不是通用的),编译器会生成一个带有&#34;期望&#34;的见证表。方法引用,即print("FlappyBird: \(name)")。由于FlappyBird不是通用的,因此其见证表不需要对doSomething()的无约束默认实现的任何引用,因此即使缺少具体类型信息,也可以正确调用约束实现。

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

  

类型的价值&#39;猫头鹰&#39;没有会员&#39; doSomething&#39;

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

参考

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

答案 1 :(得分:1)

@AllenHumphreys answer here对于部分原因是一个不错的解释。

对于修复部分,请将通用doSomething()的{​​{1}}实现为:

Owl

现在您不再需要class Owl<T> : Bird { //... func doSomething() { print("Owl<\(type(of: power))>: \(name)") } }

中的doSomething()
相关问题