Swift协议实现Equatable

34

我有以下的协议:

protocol Cacheable {
    //....//
    func identifier() -> String
}

我能让Cacheable实现Equatable接口吗?

当我执行以下操作时:

extension Cacheable: Equatable {}

func ==(lhs:Cacheable,rhs:Cacheable) -> Bool {

     return lhs.identifier() == rhs.identifier()
}

我收到了这个错误信息:扩展协议Cacheable不能有继承子句

2个回答

47

1) 允许比较两个相同类型的可缓存对象Cacheable

protocol Cacheable: Equatable {
    //....//
    func identifier() -> String
}

func ==<T : Cacheable>(lhs: T, rhs: T) -> Bool {
    return lhs.identifier() == rhs.identifier()
}

优点

这是最简单的解决方案。

缺点

你只能比较两个相同类型的Cacheable对象。这意味着下面的代码将失败,为了修复它,你需要让Animal符合Cacheable标准:

class Animal {

}

class Dog: Animal,Cacheable {
    func identifier() -> String {
        return "object"
    }
}

class Cat: Animal,Cacheable {
    func identifier() -> String {
        return "object"
    }
}

let a = Dog()

let b = Cat()

a == b //such comparison is not allowed

2) 允许比较任何类型的可缓存对象

protocol Cacheable:Equatable {
    //....//
    func identifier() -> String
}

func ==<T:Cacheable>(lhs: T, rhs: T) -> Bool {
    return lhs.identifier() == rhs.identifier()
}

func !=<T:Cacheable>(lhs: T, rhs: T) -> Bool {
    return lhs.identifier() != rhs.identifier()
}

func ==<T:Cacheable, U:Cacheable>(lhs: T, rhs: U) -> Bool {
    return lhs.identifier() == rhs.identifier()
}

func !=<T:Cacheable, U:Cacheable>(lhs: T, rhs: U) -> Bool {
    return lhs.identifier() != rhs.identifier()
}

优点

消除了解决方案1中描述的限制。现在你可以轻松地比较DogCat

缺点

  • 实现更长。实际上我不确定为什么只指定==函数不足以满足要求 - 这可能是编译器的一个bug。无论如何,你必须提供==!=的实现。
  • 在某些情况下,这种实现的好处也可能会带来问题,因为你允许比较完全不同的对象,而编译器完全没有问题。

3)不符合Equatable规范

protocol Cacheable {
    //....//
    func identifier() -> String
}

func ==(lhs: Cacheable, rhs: Cacheable) -> Bool {
    return lhs.identifier() == rhs.identifier()
}

func !=(lhs: Cacheable, rhs: Cacheable) -> Bool {
    return lhs.identifier() != rhs.identifier()
}

优点

您可以使用Cacheable作为类型,而无需任何泛型。这引入了全新的可能性。例如:

let c:[Cacheable] = [Dog(),RaceCar()]

c[0] == c[1]
c[0] != c[1]

使用解决方案1和2,此代码将失败,您必须在类中使用泛型。然而,使用最新的实现,Cacheable被视为一种类型,因此您可以声明一个类型为[Cacheable]的数组。
缺点:
您不再声明符合Equatable,因此任何接受Equatable参数的函数都不会接受Cacheable。显然,除了我们为Cacheable声明的==!=之外。
如果这在您的代码中不是问题,我实际上更喜欢这个解决方案。能够将协议视为一种类型在许多情况下非常有用。

1
之前我曾尝试过比较两个实现了Cacheable接口的对象,但是却得到了这样的错误信息:Binary operator '==' cannot be applied to two Cacheable operands - Bobj-C
2
这就是我使用泛型的原因 ;) 这段代码是可行的。我刚用Xcode 7 beta 5测试过了。 - Andriy Gordiychuk
不要使用playground。尝试将其放入代码中。将其放入您的应用程序之一的AppDelegate中,并在willFinishLaunching函数中检查它。 - Andriy Gordiychuk
2
这应该是被接受的答案。在Swift 2.1中完美运行。 - Aliaksandr Bialiauski
请注意,使用#2时,一旦Cachable扩展Equatable,它将继承一个Self引用,这意味着它不能再用作类型。例如:let x:Cachable = ... 将变得无效。这是Swift协议中的一个巨大问题,根据您的操作,可能很难甚至不可能解决。 - drekka
显示剩余7条评论

11

尝试。

extension Equatable where Self : Cacheable {
}

1
这似乎不起作用:我定义了一个类class MyClass:Cacheable {...},并尝试将该类的实例传递给函数func foo <T:Equatable>(obj:T),但无法编译。(顺便说一句:一个相同的解决方案曾经发布过,但后来被删除了,因为它不起作用。) - Martin R
相同,获取二进制运算符“==”不能应用于两个“SubtitleTrack”操作数,其中SubtitleTrack是使用此扩展名的协议:( - Steven B.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接