Swift-必须由子类覆盖的类方法


91

是否有一种标准方法可以在Swift中制作“纯虚函数”,即。一个必须由每个子类中被覆盖,并且,如果不是的话,将导致编译时错误?


您可以在超类中实现它并声明。我已经看到了在Obj-C,Java和Python中使用的代码。
David Skrundz 2014年

8
@NSArray这会导致运行时错误,而不是编译时错误
JuJoDi

这个答案也会对您有帮助。在此处输入链接描述
Chamath Jeevan

纯虚函数由protocols 实现(与interfaceJava中的s相比),如果您需要像抽象方法一样使用它们,请查看以下问题/答案:stackoverflow.com/a/39038828/2435872
jboi

Answers:


147

您有两种选择:

1.使用协议

将超类定义为协议而不是类

Pro:编译时检查每个“子类”(不是实际的子类)是否实现了所需的方法

缺点:“超类”(协议)无法实现方法或属性

2.声明该方法的超级版本

例:

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

Pro:可以在超类中实现方法和属性

缺点:不检查编译时间


3
@jewirth您仍然无法获得子类的编译时检查
drewag 2014年

5
该协议无法实现方法,但是您可以通过扩展方法来提供它们。
David Moles

2
从Swift 2.0开始,现在也有了协议扩展:) Apple Reference
Ephemera 2015年

4
虽然fatalError不提供编译时检查,但是很高兴编译器至少足够聪明,以至于不需要在执行路径调用时为方法提供返回值fatalError
bugloaf

3
情况2:请记住一个事实,即如果您super.someFunc()从重写方法中调用,尽管您已重写了该错误,但仍会收到错误。您知道您不应该称呼它,但是其他人不必知道,只需遵循标准做法即可。
雅各布·特鲁拉斯(JakubTruhlář)

48

以下允许从类继承,也可以进行协议的编译时检查:)

protocol ViewControllerProtocol {
    func setupViews()
    func setupConstraints()
}

typealias ViewController = ViewControllerClass & ViewControllerProtocol

class ViewControllerClass : UIViewController {

    override func viewDidLoad() {
        self.setup()
    }

    func setup() {
        guard let controller = self as? ViewController else {
            return
        }

        controller.setupViews()
        controller.setupConstraints()
    }

    //.... and implement methods related to UIViewController at will

}

class SubClass : ViewController {

    //-- in case these aren't here... an error will be presented
    func setupViews() { ... }
    func setupConstraints() { ... }

}

2
不错,是抢救的typealias :)
Chris Allinson

有什么方法可以阻止此API的用户从ViewControllerClass而非ViewController派生其clild类?这对我来说是一个很好的解决方案,因为几年后我将从类型别名中派生出东西,并且已经忘记了那时需要重写哪些功能。
大卫·雷克托

@David Rector,您可以将您的课程设为私有,将您的打字类型设为公开吗?很抱歉,我的手机无法发送消息。
ScottyBlades

1
完美的解决方案,谢谢您。正如@DavidRector所强调的那样,如果有一种解决方案也可以使其公开,那就太好了,这样只有typealias是公开的,但是不幸的是这似乎是不可能的。
Cyber​​Dandy

35

不支持抽象类/虚函数,但在大多数情况下,您可以使用协议:

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

如果SomeClass没有实现someMethod,则会出现此编译时错误:

error: type 'SomeClass' does not conform to protocol 'SomeProtocol'

29
请注意,这仅适用于实现协议的最高级类。任何子类都可以巧妙地忽略协议要求。
2015年

2
此外,不支持在协议上使用泛型=(
Dielson Sales

14

如果您没有太多的“虚拟”方法,另一种解决方法是让子类将“实现”作为函数对象传递给基类构造函数:

class MyVirtual {

    // 'Implementation' provided by subclass
    let fooImpl: (() -> String)

    // Delegates to 'implementation' provided by subclass
    func foo() -> String {
        return fooImpl()
    }

    init(fooImpl: (() -> String)) {
        self.fooImpl = fooImpl
    }
}

class MyImpl: MyVirtual {

    // 'Implementation' for super.foo()
    func myFoo() -> String {
        return "I am foo"
    }

    init() {
        // pass the 'implementation' to the superclass
        super.init(myFoo)
    }
}

1
如果您还有其他一些虚拟方法
它就

@ xs2bush如果您有更多的方法是虚拟的,则最好在协议中声明它们,并通过扩展方法提供“非虚拟”方法。
David Moles

1
那就是我最终要做的事情
Bushra Shahid

0

您可以使用协议VS断言在回答表明这里通过drewag。但是,缺少该协议的示例。我在这里覆盖

协议

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

现在,需要每个子类来实现在编译时检查的协议。如果SomeClass没有实现someMethod,则会出现此编译时错误:

错误:类型“ SomeClass”不符合协议“ SomeProtocol”

注意:这仅适用于实现协议的最高级类。任何子类都可以巧妙地忽略协议要求。-作为评论memmons

断言

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

但是,断言仅在运行时有效。


-2

作为iOS开发的新手,我不完全确定何时实施该方法,但是要获得两全其美的一种方法是对协议进行扩展:

protocol ThingsToDo {
    func doThingOne()
}

extension ThingsToDo {
    func doThingTwo() { /* Define code here */}
}

class Person: ThingsToDo {
    func doThingOne() {
        // Already defined in extension
        doThingTwo()
        // Rest of code
    }
}

扩展名是使您可以使用函数默认值的功能,而常规协议中的函数仍会提供编译时错误(如果未定义)


抽象函数与默认实现相反
Hogdotmac '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.