将功能附加到子类中覆盖的泛型方法

时间:2021-02-21 09:38:17

标签: swift generics

考虑这个代码片段

class Op<T> {

    func exec() -> T {
        fatalError("not implemented")
    }
}

class SubOp<U>: Op<U> {

    override func exec() -> U {
        // 1. Would produce an error: "Cannot convert return expression
        // of type '()' to return type 'U'"

        doStuff() 

        // 2. Cant call `return super.exec()` to produce a valid return
        // type because base class doesn't implement exec().
    }

    func doStuff() {
        print("stuff")
    }
}

我有一个通用基类 Op,它的 exec() 方法有一个空实现,需要在子类中重写。为了绕过那个讨厌的通用返回值,插入了一个永远不会返回的 fatalError。但是,如果子类必须为此方法添加功能,则不能,因为它必须调用 super 来创建有效的返回类型。

现在我可以要求子类调用另一个方法,例如doStuff 通过 API 合同在其通用 exec() 方法中使用,但这似乎不是一个好主意。

有没有更好的模式来解决这个问题?

PS:我知道这段代码不正确,只是为了演示它应该实现的功能。

2 个答案:

答案 0 :(得分:0)

我不太确定你的问题是什么: 如果你写

 override func exec() -> U {
        doStuff() 
    }

这肯定是不正确的,因为 doStuff 不返回任何东西(只有 void,它是 () 的别名) 如果您调用 super.exec(),您的应用程序将崩溃。我想这不是你想要的。

您是否要求类似的东西

public protocol Initable {
    init()
}

extension Int:Initable {}

class Op<T> {

    func exec() -> T {
        fatalError("not implemented")
    }
}

class SubOp<U>: Op<U> where U:Initable {

    override func exec() -> U {
        doStuff()
    }

    func doStuff() -> U {
        print("stuff")
        return U()
    }
}

let so = SubOp<Int>()
var i = so.exec()

更新

如果你不想doStuff返回一些东西,你需要写:

 override func exec() -> U {
        doStuff()
        return U()
    }

    func doStuff() {
        print("stuff")
    }

必须退货。问问芭芭拉·利斯科夫。

更新二

您也可以将闭包交给 exec,然后子类实现可以决定是否调用该闭包。 注意:您只需要符合 Initable 协议,因为 let theU = U() 中有 SubOb.exec 语句。如果您不创建任何 U 对象而是从其他地方获取它们,则可以跳过此步骤。

public protocol Initable {
    init()
}

extension Int:Initable {}


class Op<T> {

    typealias Callback = (T)->()
    
    func exec(cb:Callback) {
        // Just do nothing. Or crash:
        // fatalError("not implemented")
    }
}

class SubOp<U>: Op<U> where U:Initable {

    override func exec(cb:Callback)  {
        doStuff()
        let theU = U()
        cb(theU)
    }

    func doStuff() {
        print("stuff")
    }
}

let so = SubOp<Int>()
let intCallback:(Int)->() = {
    (intValue:Int) in
    print ("called with value \(intValue)")
}

so.exec(cb:intCallback)

答案 1 :(得分:-2)

和 Andreas 一样,我也不知道你想做什么。您为什么将 T 重命名为 U,您打算如何使用它?你为什么要使用子类化?

如果没有更多信息,我们无法判断您为什么不这样做:

final class SubOp: Op<Void> {
  override func exec() {
    print("stuff")
  }
}
相关问题