不透明类型(Opaque Type)(学习笔记)
环境Xcode 11.0 beta4 swift 5.1
-
不透明类型可解决的问题
-
如下示例,打印一个图形
protocol Shape { func draw() -> String } // struct Triangle: Shape { var size: Int func draw() -> String { var result = [String]() for length in 1...size { result.append(String(repeating: "*", count: length)) } return result.joined(separator: "\n") } } let smallTriangle = Triangle(size: 3) print(smallTriangle.draw()) // * // ** // ***
-
可以通过上面使用泛型来翻转
struct FlippedShape<T: Shape>: Shape { var shape: T func draw() -> String { let lines = shape.draw().split(separator: "\n") return lines.reversed().joined(separator: "\n") } } let flippedTriangle = FlippedShape(shape: smallTriangle) print(flippedTriangle.draw()) // *** // ** // *
-
可以用上面的两个来组装一个图形
struct JoinedShape<T: Shape, U: Shape>: Shape { var top: T var bottom: U func draw() -> String { return top.draw() + "\n" + bottom.draw() } } let joinedTriangles = JoinedShape(top: smallTriangle, bottom: flippedTriangle) print(joinedTriangles.draw()) // * // ** // *** // *** // ** // * // 对于 JoinedShape、 FlippedShape 不关心使用者,模型的公共接口由这两个操作组成,然后返回一个 Shape 类型值即可
-
-
返回不透明类型
-
可以将不透明类型看作泛型的反面,以下的函数返回类型要依据调用者
func max<T>(_ x: T, _ y: T) -> Where T: Comparable { ... }
-
下面的示例是返回一个梯形,但没有对外暴露底层的类型
struct Square: Shape { var size: Int func draw() -> String { let line = String(repeating: "*", count: size) let result = Array<String>(repeating: line, count: size) return result.joined(separator: "\n") } } // 此函数定义返回一个 some Shape 类型 func makeTrapezoid() -> some Shape { let top = Triangle(size: 2) let middle = Square(size: 2) let bottom = FlippedShape(shape: top) let trapezoid = JoinedShape( top: top, bottom: JoinedShape(top: middle, bottom: bottom) ) return trapezoid } let trapezoid = makeTrapezoid() print(trapezoid.draw()) // * // ** // ** // ** // ** // * // 结合不透明类型返回泛型 func flip<T: Shape>(_ shape: T) -> some Shape { return FlippedShape(shape: shape) } func join<T: Shape, U: Shape>(_ top: T, _ bottom: U) -> some Shape { JoinedShape(top: top, bottom: bottom) } let opaqueJoinedTriangles = join(smallTriangle, flip(smallTriangle)) print(opaqueJoinedTriangles.draw()) // * // ** // *** // *** // ** // *
-
在用不透明作返回值时,所有可能的返回值必须是相同的类型,如下是错误的
func invalidFlip<T: Shape>(_ shape: T) -> some Shape { if shape is Square { return shape // Error: return types don't match } return FlippedShape(shape: shape) // Error: return types don't match } // 上面的用 Square 调用返回是 Square,其它情况返回的是 FlippedShape 类型 // 上面一种修复的方法是将 Square 的特殊情况移动到 FlippedShape 内部实现,这样就可以统一返回类型 struct FlippedShape<T: Shape>: Shape { var shape: T func draw() -> String { if shape is Square { return shape.draw() } let lines = shape.draw().split(separator: "\n") return lines.reversed().joined(separator: "\n") } }
-
下面是将类型参数合并到返回值的底层类型中,且在下面示例中,返回值具有相同的底层类型
[T]
,它仍然遵守了具有不透明类型函数返回的类型是单一类型的要求func `repeat`<T: Shape>(shape: T, count: Int) -> some Collection { return Array<T>(repeating: shape, count: count) }
-
-
不透明类型和协议类型的不同
-
两者非常相似,主要区别在于它们是否保留类型标识符;不透明类型指的是一种特定的类型,协议类型是可以引用作保符合协议的类型;一般来讲,协议类型在存储的值的
基础类型方面更灵活,而不透明类型使对这些基础类型做出更强的保证。以下是协议类型代替不透明类型func protoFlip<T: Shape>(_ shape: T) -> Shape { return FlippedShape(shape: shape) } // 保留灵活性,可以返回多种类型 func protoFlip<T: Shape>(_ shape: T) -> Shape { if shape is Square { return shape } return FlippedShape(shape: shape) } let protoFlippedTriangle = protoFlip(smallTriangle) let sameThing = protoFlip(smallTriangle) protoFlippedTriangle == sameThing // Error // 上面一行是错的,因为协议没有实现 == 操作符,如果实现了还是错的,因为 == 操作符需要知道左边和右边参数,而通常这种操作符会有一个默认的 Self // 与采用协议的任何具体类型匹配,但当使用协议作为类型时,给协议添加 Self 需求不准类型擦除发生 // 使用协议作为返回值增加了灵活性,但返回值的一些操作就不可用,如 == 使用协议类型作返回值无法保留特定类型信息
-
有关联类型的协议不能作为返回值
protocol Container { associatedtype Item var count: Int { get } subscript(i: Int) -> Item { get } } extension Array: Container { } // Error: Protocol with associated types can't be used as a return type. func makeProtocolContainer<T>(item: T) -> Container { return [item] } // // Error: Not enough information to infer C. func makeProtocolContainer<T, C: Container>(item: T) -> C { return [item] }
-
不透明类型作为返回类型表达式
func makeOpaqueContainer<T>(item: T) -> some Container { return [item] } let opaqueContainer = makeOpaqueContainer(item: 12) let twelve = opaqueContainer[0] print(type(of: twelve)) // Prints "Int" // 说明不透明类型和类型推断一起使用,传入12,可以推导出底层类型 [Int]
-
网友评论