在 Swift 中指定协议之间的关系

Specify relationships between protocols in Swift

我想指定一个协议来管理一些符合另一个协议的类型对象。像这样:

// Specify protocol
protocol ElementGenerator {
    func getElements() -> [Element]
}
protocol Element {
    // ...
}

// Implement
class FooElementGenerator: ElementGenerator {
    func getElements() -> [FooElement] {
        // Generate elements here
        return [FooElement()]
    }
}
class FooElement {
    // ...
}

尝试编译时,出现错误:

Type 'FooElementGenerator' does not conform to protocol 'ElementGenerator'

暗示候选人 func getElements() -> [FooElement] 具有不匹配的类型 () -> [FooElement],但它期望 () -> [Element].

如何修复这种错误?

更新:

此解决方案似乎有效:

protocol ElementGenerator {
    typealias T:Element

    func getElements() -> [T]
}

protocol Element {
    // ...
}


class FooElementGenerator: ElementGenerator {
    typealias T = FooElement

    func getElements() -> [T] {
        return [T()]
    }
}

class FooElement: Element {
    // ...
}

但是当我尝试创建这样的变量时:

let a: ElementGenerator = FooElementGenerator()

出现新错误:

Protocol 'ElementGenerator' can only be used as a generic constraint because it has Self or associated type requirements

实现协议方法时,return 类型必须相同,但您可以 return 子对象 class 像这样;

protocol ElementGenerator {
    func getElements() -> [Element]
}

//@objc for bridging in objective C
@objc protocol Element {
    // ...
}

// Implement
class FooElementGenerator: NSObject,ElementGenerator {

    override init() {
        super.init();
        //--
        let fooElements:[FooElement] = self.getElements() as! [FooElement]
    }

    func getElements() -> [Element] {
        // Generate elements here
        return [FooElement()]
    }
}

class FooElement:NSObject, Element {
    // ...
    override init() {
        super.init();
        //--
        NSLog("FooElement init");
    }
}

第二种情况的错误信息是因为你定义了ElementGenerator一个“关联类型”,这意味着你只能用它来给出类型的约束。

例如,如果您需要为通用 ElementGenerator 值定义一个函数,您可以这样写:

func f<T1:ElementGenerator>(elemGenerator:T1) -> Element {
  return elemGenerator.getElements()[0]
}

var a : Element = FooElementGenerator()
var b : Element = BarElementGenerator()

var x : Element = f(a)
var y : Element = f(b)

var z : FooElement = f(a) as! FooElement