返回不透明类型(中)
in 技术 with 0 comment

返回不透明类型(中)

in 技术 with 0 comment

您可以将不透明类型视为与泛型类型相反的类型。泛型类型,以一种从函数实现中抽象出来的方式,让调用函数的代码选择该函数参数和返回值的类型。而不透明类型允许函数实现以一种从函数内部的代码中抽象出来的方式为它返回的值选择类型。

返回不透明类型

例如,以下代码中的函数返回一个依赖于其调用者的类型:

func max<T>(_ x: T, _ y: T) -> T where T: Comparable { ... }

max(_:_:) 函数的调用者传递 x 和 y 的值,这些值的类型决定了 T 的具体类型。函数的调用者可以使用任何符合 Comparable 协议的类型。函数内部的代码以通用方式编写,因此它可以处理调用者传递的任何类型。实现 max(_:_:) 仅使用到了所有 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")
    }
}

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())
// *
// **
// **
// **
// **
// *

本例中的 makeTrapezoid() 函数将其返回类型声明为 some Shape。因此,该函数返回一个符合 Shape 协议的给定类型的值,而不指定任何特定的具体类型。以这种方式编写 makeTrapezoid() 可以让它表达其公共接口的基础部分——它返回的值是一个形状,而不需要指定形状是由其公共接口的一部分构成的特定类型。此实现使用两个三角形和一个正方形,但可以重写该函数以在不更改其返回类型的情况下以各种其他方式绘制梯形。

此示例突出显示了不透明返回类型与泛型类型相反的方式。 makeTrapezoid() 中的代码可以返回它需要的任何类型,只要该类型符合 Shape 协议,就像调用泛型函数的代码一样。调用该函数的代码需要以通用方式编写,比如泛型函数的实现,这样它就可以使用 makeTrapezoid() 返回的任何 Shape 值。

您还可以将不透明的返回类型与泛型结合起来。以下代码中的函数都返回符合 Shape 协议的不透明类型的值。

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())
// *
// **
// ***
// ***
// **
// *

此示例中 opaqueJoinedTriangles 的值与本章前面的“不透明类型解决的问题”部分中泛型示例中的 joinTriangles 相同。但是,与该示例中的值不同,flip(_:) 和 join(_:_:) 将泛型形状操作返回的底层类型包装在不透明的返回类型中,从而防止这些类型可见。这两个函数都是泛型的,因为它们依赖的类型是泛型的,并且函数的类型参数传递了 FlippedShape 和 JoinedShape 所需的类型信息。

如果具有不透明返回类型的函数从多个位置返回,则所有可能的返回值必须具有相同的类型。

对于泛型函数,该返回类型可以使用函数的泛型类型参数,但它仍然必须是单一类型。例如,这是形状翻转函数的无效版本,其中包括正方形的特殊情况:

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。这违反了仅返回一种类型的值的要求,并使 invalidFlip(_:) 函数无效。修复 invalidFlip(_:) 的一种方法是将正方形的特殊情况移动到 FlippedShape 的实现中,这让该函数始终返回一个 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")
    }
}

始终返回单一类型的要求并不妨碍您在不透明的返回类型中使用泛型。下面是一个函数示例,该函数将其类型参数合并到它返回的值的基础类型中:

func `repeat`<T: Shape>(shape: T, count: Int) -> some Collection {
    return Array<T>(repeating: shape, count: count)
}

在这种情况下,返回值的基础类型取决于 T:,无论传递给它什么形状,repeat(shape:count:) 都会创建并返回该形状的数组。尽管如此,返回值始终具有相同的基础类型 [T],因此它遵循具有不透明返回类型的函数必须仅返回单一类型的值的要求。

参考:https://docs.swift.org/swift-book/LanguageGuide/OpaqueTypes.html

Responses