有没有办法将“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
因为 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>
类型吗?
对于泛型类型 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
的实现。
参考资料
是对原因部分的恰当解释。
至于修复部分,将通用 Owl
的 doSomething()
实现为:
class Owl<T> : Bird {
//...
func doSomething() {
print("Owl<\(type(of: power))>: \(name)")
}
}
现在您在 extension Bird where Self: Owl<String>
中不再需要 doSomething()
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>
类型吗?
对于泛型类型 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]
.
在 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
的实现。
参考资料
至于修复部分,将通用 Owl
的 doSomething()
实现为:
class Owl<T> : Bird {
//...
func doSomething() {
print("Owl<\(type(of: power))>: \(name)")
}
}
现在您在 extension Bird where Self: Owl<String>
doSomething()