美文网首页swift
《Swift从入门到精通》(二十):不透明类型

《Swift从入门到精通》(二十):不透明类型

作者: 萧1帅 | 来源:发表于2021-10-19 09:05 被阅读0次

    不透明类型(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]
        
        

    相关文章

      网友评论

        本文标题:《Swift从入门到精通》(二十):不透明类型

        本文链接:https://www.haomeiwen.com/subject/ntrnoltx.html