【问题标题】:Overriding equals with generic class用泛型类覆盖等于
【发布时间】:2015-03-15 21:09:13
【问题描述】:

我创建了一个旨在用作“抽象类”的类(仅用于子类化,不直接实例化)。由于 Swift 不支持这一点,因此必须使用例如抽象方法体中的致命错误。

我的抽象类必须是等价的。所以我想,我在 equals 方法中使用了 fatalError:

class MySuperClass:Equatable {
}

func ==(lhs: MySuperClass, rhs: MySuperClass) -> Bool {
    fatalError("Must override")
}

class MySubClass:MySuperClass {
    let id:Int

    init(_ id:Int) {
        self.id = id
    }
}

func ==(lhs: MySubClass, rhs: MySubClass) -> Bool {
    return lhs.id == rhs.id
}


let a = MySubClass(1)
let b = MySubClass(2)
let c = MySubClass(2)

a == b
b == c

这很有效。虽然我的子类有一个类型参数,但我有一个小问题。现在示例如下所示:

class MySuperClass:Equatable {
}

func ==(lhs: MySuperClass, rhs: MySuperClass) -> Bool {
    fatalError("Must override")
}

class MySubClass<T>:MySuperClass {
    let id:Int

    init(_ id:Int) {
        self.id = id
    }
}

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


let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)

a == b
b == c

现在它崩溃了,因为它没有“看到”覆盖的 equals,它只执行超类中的 equals。

我知道 Swift 在使用泛型类型的覆盖方面存在一些问题。我认为这仅限于与 obj-c 的交互。这至少看起来像是语言缺陷或错误,如果 B 是 A 的子类,为什么泛型 B 类的 equals 不会覆盖 A 类的 equals?

【问题讨论】:

  • 抱歉,我最初误读了这个问题——这不是 stackoverflow.com/q/28793218/3925941 的欺骗,因为你问的是重载和泛型而不是动态调度。尽管该问题中涵盖的大部分内容也适用于平等和继承层次结构。

标签: swift generics


【解决方案1】:

正如 Airspeed 所暗示的,问题在于操作符的实现不是类/结构实现的一部分 => 因此,继承在那里不起作用。

你能做的就是将逻辑留在类实现的内部,让操作者使用它。例如。以下将满足您的需求:

class MySuperClass: Equatable {

    func isEqualTo(anotherSuperClass: MySuperClass) -> Bool {
        fatalError("Must override")
    }

}

func == (lhs: MySuperClass, rhs: MySuperClass) -> Bool {
    return lhs.isEqualTo(rhs)
}

class MySubClass<T>:MySuperClass {

    let id: Int

    init(_ id: Int) {
        self.id = id
    }

    override func isEqualTo(anotherSuperClass: MySuperClass) -> Bool {
        if let anotherSubClass = anotherSuperClass as? MySubClass<T> {
            return self.id == anotherSubClass.id
        }

        return super.isEqualTo(anotherSuperClass) // Updated after AirSpeed remark
    }

}

let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)

a == b
b == c

... 如您所见,== 运算符仅定义一次,它使用MySuperClass 的方法来确定其两个参数是否相等。然后.isEqualTo() 处理剩下的事情,包括在MySubClass 级别上使用继承机制。

UPD

上述方法的好处是以下方法仍然有效:

let a2: MySuperClass = a
let b2: MySuperClass = b
let c2: MySuperClass = c

a2 == b2
b2 == c2

...即,无论编译时的变量类型如何,行为都将由实际的实例类型决定。

【讨论】:

  • 现在没有时间浏览所有内容,但事先很少观察,关于您的第一条评论 - 不知何故,继承似乎起作用,正如您在不使用泛型的示例中看到的那样.只有在使用泛型时才会出现问题。
  • 这种方法的问题现在是操作符参数的顺序改变了行为:如果你声明 let a = MySuperClass(); let b = MySubClass&lt;Any&gt;(2) 然后 a == b 断言,但 b == a 没有。这是不行的,因为Equatable 类必须保证== 是对称的。
  • @Ixx:在这种情况下工作的不是继承,而是基于类型的绑定(参见 Airspeed 的答案)。
  • @AirspeedVelocity:同意。为了完全对称,应该使用return super.isEqualTo(anotherSuperClass) 而不是return false
【解决方案2】:

在重载决议中,非泛型函数始终优先于泛型函数,因此不考虑采用子类的函数优先于采用超类的函数的较小规则。

一个可能的解决方案是使超类== 也通用。这样,在两个通用函数之间进行选择的规则就会生效,在这种情况下,更具体的一个是采用由 T 参数化的特定类的那个:

func ==<T: MySuperClass>(lhs: T, rhs: T) -> Bool {
    // bear in mind, this is a question of compile-time overloading,
    // rather than overriding
    fatalError("Must override")
}

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

let a = MySubClass<Any>(1)
let b = MySubClass<Any>(2)
let c = MySubClass<Any>(2)

// no longer asserts
a == b
b == c

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-09
    • 2019-08-12
    • 1970-01-01
    • 2017-01-11
    • 1970-01-01
    • 2012-03-01
    • 2020-03-14
    • 1970-01-01
    相关资源
    最近更新 更多