具有自我类型要求的协议的类型擦除

罗德里戈·鲁伊斯(Rodrigo RuizMurguía)

我有一个Shape符合的协议Comparable最终,即使它们不是同一子类型,我仍然希望创建一个符合该协议的数组。

我创建了一些类符合Shape,即TriangleSquareRectangle我想做的是定义另一个Drawing可以接受任何Shape数组的

//: Playground - noun: a place where people can play
import Foundation
import UIKit

protocol Shape: Comparable {
    var area: Double { get }
}

extension Shape {
    static func < (lhs: Self, rhs: Self) -> Bool {
        return lhs.area < rhs.area
    }

    static func == (lhs: Self, rhs: Self) -> Bool {
        return lhs.area == rhs.area
    }
}

class Triangle: Shape {
    let base: Double
    let height: Double

    var area: Double { get { return base * height / 2 } }

    init(base: Double, height: Double) {
        self.base = base
        self.height = height
    }
}

class Rectangle: Shape {
    let firstSide: Double
    let secondSide: Double

    var area: Double { get { return firstSide * secondSide } }

    init(firstSide: Double, secondSide: Double) {
        self.firstSide = firstSide
        self.secondSide = secondSide
    }
}

class Square: Rectangle {
    init(side:  Double) {
        super.init(firstSide: side, secondSide: side)
    }
}

class Drawing {
    //Protocol 'Shape' can only be used as a generic constraint because it has Self or associated type requirements
    let shapes: [Shape]
    init(shapes: [Shape]) {
        self.shapes = shapes
    }
}

但是,当我尝试使用Shape数组的类型时,出现以下错误Protocol 'Shape' can only be used as a generic constraint because it has Self or associated type requirements如何声明包含任何类型的形状的数组?

罗布·纳皮尔

您几乎碰到了在设计协议时可能遇到的每个基本错误,但是它们都是极为常见的错误,不足为奇。每个人在开始时都以这种方式进行操作,并且需要一段时间才能使您的头脑处于正确的位置。我已经思考这个问题将近四年了,并就这个问题进行了演讲,但我仍然搞砸了它,必须备份并重新设计协议,因为我犯了同样的错误。

协议不能代替抽象类。协议有两种完全不同的类型:简单协议和PAT(具有关联类型的协议)。

一个简单的协议代表一个具体的接口,可以用其他语言中使用抽象类的某些方式来使用,但最好将其视为一系列要求。就是说,您可以将一个简单的协议视为一种类型(它实际上已成为一种存在的协议,但是非常接近)。

PAT是用于约束其他类型的工具,因此您可以为这些类型提供其他方法,或将其传递给通用算法。但是PAT不是一种类型。它不能放在数组中。无法将其传递给函数。它不能保存在变量中。这不是一种类型。没有“可比”之类的东西。有些类型符合Comparable。

可以使用类型的橡皮擦将PAT强制转换为具体的类型,但这几乎总是一个错误并且非常不灵活,如果您必须发明一种新型的橡皮擦来做到这一点尤其糟糕。通常(假设有例外),假设您要使用类型橡皮擦,则可能是设计了错误的协议。

当您将“可比”(并通过“等于”)定为Shape的要求时,您说Shape是PAT。你不想那样 但是话又说回来,您不想要Shape。

很难确切知道如何设计它,因为您没有显示任何用例。协议来自用例。它们通常不会从模型中产生。因此,我将提供您入门的方法,然后我们将讨论如何根据您的需要实施其他工作。

首先,您可以将这些形状建模为值类型。它们只是数据。没有理由引用语义(类)。

struct Triangle: Equatable {
    var base: Double
    var height: Double
}

struct Rectangle: Equatable {
    var firstSide: Double
    var secondSide: Double
}

我删除了Square,因为这是一个非常糟糕的例子。在继承模型中,正方形不是正确的矩形(请参见Circle-Ellipse问题)。您碰巧使用不可变数据来摆脱它,但是不可变数据不是Swift中的规范。

看来您想计算这些区域,因此我假设有一些算法对此很在意。它可以在“提供区域的区域”上工作。

protocol Region {
    var area: Double { get }
}

我们可以说,通过追溯建模,三角形和矩形与Region一致。这可以在任何地方完成;创建模型时不必决定。

extension Triangle: Region {
    var area: Double { get { return base * height / 2 } }
}

extension Rectangle: Region {
    var area: Double { get { return firstSide * secondSide } }
}

现在Region是一个简单的协议,因此将其放入数组中没有问题:

struct Drawing {
    var areas: [Region]
}

剩下最初的平等问题。那有很多微妙之处。第一个也是最重要的一点是,在Swift中,“等于”(至少在与Equatable协议绑定时)意味着“可以用于任何目的”。因此,如果您说“三角形==矩形”,则必须表示“在可以使用该三角形的任何情况下,您都可以自由使用矩形”。它们恰好具有相同的面积这一事实似乎并不是定义该替代的非常有用的方法。

同样,说“三角形小于矩形”也没有意义。有意义的是说三角形的面积小于矩形的面积,但这仅表示Area符合的类型Comparable,而不是形状本身。(在您的示例中,Area等效于Double。)

肯定有前进方法来测试地区之间的平等(或类似的平等),但这在很大程度上取决于您打算如何使用它。它不是自然地从模型中产生的。这取决于您的用例。Swift的强大之处在于,它允许相同的模型对象符合许多不同的协议,从而支持许多不同的用例。

如果您可以在本示例中提供进一步的说明(调用代码将是什么样子),那么我可以对此进行扩展。特别要从充实Drawing一点开始如果您从不访问该数组,那么放进它就没关系。for在该数组上,理想的循环是什么样的?

您正在处理的示例几乎与最著名的面向协议的编程对话中使用的示例:Swift中的面向协议的编程,也称为“ Crusty对话”。这是开始了解如何在Swift中思考的好地方。我相信它将引发更多问题。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Swift-协议只能用作通用约束,因为它具有“自我”或相关类型要求

具有通用枚举和通用协议的Swift类型擦除

具有相关类型要求和默认实现的Swift协议

具有通用自我类型的Scala特征

Swift类型检查器拒绝将具有类要求的协议作为类类型

类型“ SwiftClass”不能符合协议“ ObjcProtocol”,因为它具有无法满足的要求

协议“行”只能用作通用约束,因为它具有“自身”或关联的类型要求

协议“形状”只能用作通用约束,因为它具有 Self 或相关类型要求

协议的声明属性显示协议只能用作一般约束,因为它具有“自身”或关联的类型要求

实施具有不同关联类型的协议

具有相同关联类型名称的协议

具有通用参数类型的协议

具有关联类型的返回协议

具有关联类型的协议继承

Swift - 继承协议关联的类型擦除

使用具有协议和关联类型的通用类型?

符合具有关联类型的协议的枚举类型数组

具有任何类型和关联类型的协议功能

引用具有擦除类型(void *)的指针

流:要求多态类型具有属性

如何在使用关联类型的协议中使用类型擦除

如何修复协议只能用作通用约束,因为它具有 Self 或相关类型要求错误

“协议...只能用作通用约束,因为它具有Self或关联的类型要求”是什么意思?

指特征的自我类型

关联类型的自我生命

Swift元类型(类型,自我)

Java类型擦除会擦除我的通用类型吗?

Partitioner 类型的 getPartition 的名称冲突在 MapReduce、Hadoop 中具有相同的主类类型擦除

具有类型别名的协议中的AssociatedType