类型约束中的多个协议

时间:2016-12-07 13:49:23

标签: swift generics

我想使用泛型类和类型约束:

class MyCustomClass<T : Equatable> {
  var a: Array<T>
  init() {
    a = Array<T>()
  }
}

工作正常。但是如果我想使用第二个协议,那将会发生什么,例如

class MyCustomClass<T : Equatable, IndexableBase> {
  var a: Array<T>
  init() {
    a = Array<T>()
  }
}

它表示初始化程序失败,因为我必须使用2而不是1个参数。我不明白。

2 个答案:

答案 0 :(得分:17)

Swift 3

(我在您的示例中将IndexableBase替换为Collection,您应该更喜欢后者)

根据Swift 3,协议组合使用中缀运算符&而不是之前的protocol<...>结构,如接受和实现的进化提议中所述:

因此,使用协议组合,您可以将组合类型约束放在参数列表中的T占位符中:

class MyCustomClass<T: Equatable & Collection> { /* ... */ }

作为协议组合的替代方法,您还可以使用where子句来连接多个类型(和子类型)约束。根据已接受和实施的进化提案:

where子句已移至声明的末尾,在这种情况下,您的示例将显示为:

class MyCustomClass<T: Equatable> where T: Comparable { /* ... */ }

或者,甚至将完整的协议组合与声明

末尾的where子句放在一起
class MyCustomClass<T> where T: Equatable & Comparable { /* ... */ }

我们应该选择哪种风格?

有趣的讨论是我们应该考虑的“最佳实践”,因为这个特定主题没有指定Swift API指南。我们放置

  • 参数列表中的所有(主要类型)约束,或
  • 声明结尾处的所有约束,或
  • 在参数列表中拆分一些约束,在声明的末尾放置其他约束?

考虑以下设计的示例,其中我们有一个构建协议,我们计划将其用作类型约束(Doable),它本身包含associatedtype我们可以放置一个类型约束

protocol Doable { 
    associatedtype U
}

使用上面的不同方法,以下三个选项都是有效的,语法

// alternative #1
func foo<T: Equatable & Doable>(_ bar: T) -> () where T.U: Comparable { /* ... */ }

// alternative #2
func foo<T: Equatable>(_ bar: T) -> () where T: Doable, T.U: Comparable { /* ... */ }

// alternative #3
func foo<T>(_ bar: T) -> () where T: Equatable & Doable, T.U: Comparable { /* ... */ }

我将引用来自the evolution thread that that brought forth SE-0081的Apple dev Joe Groff:

  

这是一个判断电话。这是我的感觉,在许多情况下,通用   参数受至少一个重要协议或基础的约束   这是值得提前调用的课程,所以允许这样做是合理的   

之类的东西
func foo<C: Collection>(x: C) -> C.Element 
     

没有将Collection约束从前面拉得太远   声明。

因此,在上面的设计示例中,将协议组合用于直接应用于通用占位符T的类型约束并将这些约束放在参数列表中,而{的子类型约束可能是合适的。 {1}}放在声明的末尾。即,上面的替代#1。

答案 1 :(得分:1)

您可以使用此解决方法

class MyCustomClass<T: Equatable where T: IndexableBase > {
    var a: Array<T>
    init() {
        a = Array<T>()
    }
}
斯威夫特4:

class MyCustomClass<T: Equatable> where T: Collection  {
    var a: Array<T>
    init() {
        a = Array<T>()
    }
}