Swift语言中的抽象函数

时间:2014-06-08 20:11:53

标签: inheritance abstract-class swift

我想用快速语言创建一个抽象函数。有可能吗?

class BaseClass {
    func abstractFunction() {
        // How do I force this function to be overridden?
    }
}

class SubClass : BaseClass {
    override func abstractFunction() {
        // Override
    }
}

11 个答案:

答案 0 :(得分:175)

Swift中没有抽象概念(比如Objective-C),但你可以这样做:

class BaseClass {
    func abstractFunction() {
        preconditionFailure("This method must be overridden") 
    } 
}

class SubClass : BaseClass {
     override func abstractFunction() {
         // Override
     } 
}

答案 1 :(得分:33)

你想要的不是基类,而是协议。

protocol MyProtocol {
    func abstractFunction()
}

class MyClass : MyProtocol {
    func abstractFunction() {
    }
}

如果你没有在你的类中提供abstractFunction,那就是错误。

如果您仍需要基类用于其他行为,则可以执行以下操作:

class MyClass : BaseClass, MyProtocol {
    func abstractFunction() {
    }
}

答案 2 :(得分:28)

我将一个支持抽象基类的平台的大量代码移植到Swift并运行到很多。如果你真正想要的是抽象基类的功能,那么这意味着这个类既作为基于共享的类功能的实现(否则它只是一个接口/协议)并且它定义了必须由派生类。

要在Swift中执行此操作,您需要一个协议和一个基类。

protocol Thing
{
    func sharedFunction()
    func abstractFunction()
}

class BaseThing
{
    func sharedFunction()
    {
        println("All classes share this implementation")
    }
}

请注意,基类实现了共享方法,但没有实现协议(因为它没有实现所有方法)。

然后在派生类中:

class DerivedThing : BaseThing, Thing 
{
    func abstractFunction() 
    {
        println("Derived classes implement this");
    }
}

派生类从基类继承sharedFunction,帮助它满足协议的这一部分,协议仍然要求派生类实现abstractFunction。

这种方法唯一真正的缺点是,由于基类没有实现协议,如果你有一个需要访问协议属性/方法的基类方法,你必须在派生类中重写它,并且从那里调用基类(通过超级)传递self,以便基类具有协议的实例,用于执行其工作。

例如,假设需要调用abstractFunction的sharedFunction。协议将保​​持不变,类现在看起来像:

class BaseThing
{
    func sharedFunction(thing: Thing)
    {
        println("All classes share this implementation")
        thing.abstractFunction()
    }
}

class DerivedThing : BaseThing, Thing 
{
    func sharedFunction()
    {
        super.sharedFunction(self)
    }

    func abstractFunction() 
    {
        println("Derived classes implement this");
    }
}

现在派生类的sharedFunction满足协议的那部分,但派生类仍然能够以一种相当直接的方式共享基类逻辑。

答案 3 :(得分:20)

这个似乎是Apple在UIKit中处理抽象方法的“官方”方式。 请查看UITableViewController以及与UITableViewDelegate合作的方式。您要做的第一件事就是添加一行:delegate = self。嗯,这正是诀窍。

1.将抽象方法放在协议中
protocol AbstractMethodsForClassX {
    func abstractMethod() -> String
}
2.写你的基类
/// It takes an implementation of the protocol as property (same like the delegate in UITableViewController does)
/// And does not implement the protocol as it does not implement the abstract methods. It has the abstract methods available in the `delegate`
class BaseClassX {
    var delegate: AbstractMethodsForClassX!

    func doSomethingWithAbstractMethod() -> String {
        return delegate.abstractMethod() + " - And I believe it"
    }
}
3.编写子类。
/// First and only additional thing you have to do, you must set the "delegate" property
class ClassX: BaseClassX, AbstractMethodsForClassX {
    override init() {
        super.init()
        delegate = self
    }

    func abstractMethod() -> String {return "Yes, this works!"}
}
这是,你如何使用这一切
let x = ClassX()
x.doSomethingWithAbstractMethod()

使用Playground查看输出。

一些备注

  • 首先,已经给出了很多答案。我希望有人能够一路找到这个。
  • 问题实际上是,找到一个实现的模式:
    • 一个类调用一个方法,该方法必须在其派生的子类之一中实现(重写)
    • 在最好的情况下,如果子类中未覆盖该方法,则在编译时
    • 期间出错
  • 关于抽象方法的事情是,它们是接口定义和基类中实际实现的一部分的混合。两者同时进行。由于swift是非常新的且非常清晰的定义,它没有这样的便利,但是“不洁”的概念(还)。
  • 对我来说(一个可怜的老Java家伙),这个问题时有发展。我已经阅读了这篇文章中的所有答案,这次我觉得我找到了一个看似可行的模式 - 至少对我而言。
  • 更新:看起来Apple的UIKit实施者使用相同的模式。 UITableViewController通过明确设置UITableViewDelegate属性来实现delegate但仍需要注册为委托。
  • 这一切都在Xcode 7.3.1的Playground上进行了测试

答案 4 :(得分:7)

这样做的一种方法是使用基类中定义的可选闭包,孩子们可以选择是否实现它。

class BaseClass {
    var abstractClosure?:(()->())?
    func someFunc()
    {
        if let abstractClosure=abstractClosure
        {
            abstractClosure()
        }
    } 
}

class SubClass : BaseClass {
    init()
    {
        super.init()
        abstractClosure={ ..... }
    }
}

答案 5 :(得分:5)

我理解你现在在做什么,我认为你最好不要使用协议

protocol BaseProtocol {
    func abstractFunction()
}

然后,您只需遵守协议:

class SubClass : BaseProtocol {

    func abstractFunction() {
        // Override
        println("Override")
    }
}

如果你的类也是一个子类,协议遵循超类:

class SubClass: SuperClass, ProtocolOne, ProtocolTwo {}

答案 6 :(得分:4)

嗯,我知道我迟到了,我可能正在利用已经发生的变化。对不起。

无论如何,我想提供我的答案,因为我喜欢做测试,fatalError()的解决方案是,AFAIK,不可测试,有例外的解决方案更难测试。

我建议使用更多 swifty 方法。您的目标是定义一个具有一些共同细节的抽象,但这个抽象没有完全定义,即抽象方法。使用定义抽象中所有预期方法的协议,包括已定义的方法,以及未定义的方法。然后创建一个协议扩展,实现在您的案例中定义的方法。最后,任何派生类都必须实现协议,这意味着所有方法,但是作为协议扩展的一部分的方法已经实现了它们。

使用一个具体功能扩展您的示例:

protocol BaseAbstraction {
    func abstractFunction() {
        // How do I force this function to be overridden?
    }
}

extension BaseAbstraction {
    func definedFunction() {
        print("Hello")
}

class SubClass : BaseAbstraction {
    func abstractFunction() {
        // No need to "Override". Just implement.
    }
}

请注意,通过执行此操作,编译器再次成为您的朋友。如果方法没有被“覆盖”,那么在编译时会出现错误,而不是在fatalError()处获得的错误或在运行时发生的异常。

答案 7 :(得分:3)

使用assert关键字强制执行抽象方法:

class Abstract
{
    func doWork()
    {
        assert(false, "This method must be overriden by the subclass")
    }
}

class Concrete : Abstract
{
    override func doWork()
    {
        println("Did some work!")
    }
}

let abstract = Abstract()
let concrete = Concrete()

abstract.doWork()    // fails
concrete.doWork()    // OK

然而,正如 Steve Waddicor 所提到的,你可能想要一个protocol

答案 8 :(得分:2)

我理解这个问题并且正在寻找相同的解决方案。协议与抽象方法不同。

在协议中,您需要指定您的类符合此类协议,抽象方法意味着您必须覆盖此类方法。

换句话说,协议是一种可选项,你需要指定基类和协议,如果你没有指定协议,那么你就不必重写这些方法。

抽象方法意味着您需要一个基类但需要实现自己的方法或两个方法,这是不一样的。

我需要相同的行为,这就是我寻找解决方案的原因。我猜Swift缺少这样的功能。

答案 9 :(得分:1)

这个问题还有另一种选择,尽管与@ jaumard的提议相比仍存在不利因素;它需要一份退货声明。虽然我错过了要求它的要点,因为它包含直接抛出异常:

class AbstractMethodException : NSException {

    init() {
        super.init(
            name: "Called an abstract method",
            reason: "All abstract methods must be overriden by subclasses",
            userInfo: nil
        );
    }
}

然后:

class BaseClass {
    func abstractFunction() {
        AbstractMethodException.raise();
    }
}

无论之后发生什么都无法到达,所以我不明白为什么要强行回归。

答案 10 :(得分:1)

我不知道它是否会有用,但我在尝试构建SpritKit游戏时遇到了抽象方法的类似问题。我想要的是一个抽象的Animal类,它有诸如move(),run()等方法,但是sprite名称(和其他功能)应该由class children提供。所以我最终做了这样的事情(测试了Swift 2):

import SpriteKit

// --- Functions that must be implemented by child of Animal
public protocol InheritedAnimal
{
    func walkSpriteNames() -> [String]
    func runSpriteNames() -> [String]
}


// --- Abstract animal
public class Animal: SKNode
{
    private let inheritedAnimal: InheritedAnimal

    public init(inheritedAnimal: InheritedAnimal)
    {
        self.inheritedAnimal = inheritedAnimal
        super.init()
    }

    public required init?(coder aDecoder: NSCoder)
    {
        fatalError("NSCoding not supported")
    }

    public func walk()
    {
        let sprites = inheritedAnimal.walkSpriteNames()
        // create animation with walking sprites...
    }

    public func run()
    {
        let sprites = inheritedAnimal.runSpriteNames()
        // create animation with running sprites
    }
}


// --- Sheep
public class SheepAnimal: Animal
{
    public required init?(coder aDecoder: NSCoder)
    {
        fatalError("NSCoding not supported")
    }

    public required init()
    {
        super.init(inheritedAnimal: InheritedAnimalImpl())
    }

    private class InheritedAnimalImpl: InheritedAnimal
    {
        init() {}

        func walkSpriteNames() -> [String]
        {
            return ["sheep_step_01", "sheep_step_02", "sheep_step_03", "sheep_step_04"]
        }

        func runSpriteNames() -> [String]
        {
            return ["sheep_run_01", "sheep_run_02"]
        }
    }
}