覆盖等于泛型类

时间:2015-03-15 09:34:00

标签: swift generics

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

我的抽象类必须是等同的。所以我想,我在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

现在它崩溃了,因为它没有&#34;看到&#34;重写等于,它只在超类中执行等号。

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

2 个答案:

答案 0 :(得分:1)

在重载分辨率中,非泛型函数总是优先于泛型函数,因此不考虑将采用子类的函数放在超类之前的较小规则。

一种可能的解决方案是使超类==也是通用的。这样,两个泛型函数之间的选择规则就开始了,在这种情况下,更具体的一个是采用由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

答案 1 :(得分:1)

正如Airspeed所说,问题在于运算符的实现不是类/结构实现的一部分=&gt;因此,继承在那里不起作用。

您可以做的是保持类实现中的逻辑并让运算符使用它。例如。以下将满足您的需求:

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

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