快速语言中的抽象函数

我想用快速语言创建一个抽象函数,可以吗?

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


class SubClass : BaseClass {
override func abstractFunction() {
// Override
}
}
58270 次浏览

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

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


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

我知道你现在在做什么,我觉得你最好还是遵守规定

protocol BaseProtocol {
func abstractFunction()
}

然后,你只要遵守规定:

class SubClass : BaseProtocol {


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

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

class SubClass: SuperClass, ProtocolOne, ProtocolTwo {}

您需要的不是基类,而是协议。

protocol MyProtocol {
func abstractFunction()
}


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

如果你没有在你的类中提供抽象函数,这是一个错误。

如果你仍然需要其他行为的基类,你可以这样做:

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

使用 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 Waddor所提到的,您可能需要的是 protocol

这个问题还有另一个替代方案,尽管与@jaumard 的提议相比仍然存在缺点; 它需要一个 return 语句。尽管我没有理解需要它的意义,因为它包括直接抛出一个异常:

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();
}
}

无论之后发生什么,都是遥不可及的,所以我不明白为什么要强迫他们回来。

我从一个支持抽象基类的平台上向 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");
}
}

派生类从基类继承 sharedFunctional,帮助它满足协议的这一部分,而协议仍然需要派生类来实现抽象函数。

这个方法唯一真正的缺点是,由于基类不实现协议,如果你有一个需要访问协议属性/方法的基类方法,你必须在派生类中覆盖它,然后从那里调用基类(通过 super)传递 self,这样基类就有一个协议的实例来执行它的工作。

例如,让我们假设 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 满足了协议的这一部分,但是派生类仍然能够以一种相当直接的方式共享基类逻辑。

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

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


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

我不知道它是否有用,但是我在尝试构建 SpritKit 游戏时遇到了类似的抽象方法问题。我想要的是一个抽象 Animal 类,它具有 move ()、 run ()等方法,但是 sprite 名称(和其他功能)应该由类的子类提供。所以我最终做了这样的事情(在 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"]
}
}
}

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

在协议中,您需要指定您的类符合这样的协议,抽象方法意味着您必须重写这样的方法。

换句话说,协议是可选的,你需要指定基类和协议,如果你没有指定协议,那么你就不需要覆盖这些方法。

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

我需要同样的行为,这就是为什么我在寻找一个解决方案。我想斯威夫特缺少这样的特点。

我知道我迟到了我可能在利用已经发生的变化。不好意思。

在任何情况下,我想贡献我的答案,因为我喜欢做测试和解决方案的 fatalError()是,AFAIK,不可测试和那些与例外是更难测试。

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

用一个具体的函数扩展你的例子:

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()或运行时发生的异常所导致的错误。

这似乎是苹果在 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"
}
}
写子类(es)。
/// 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 的输出。

一些评论

  • 首先,我们已经给出了很多答案。我希望有人能找到这个问题的答案。
  • 问题实际上是,要找到一种实现以下目标的模式:
    • 类调用方法,该方法必须在其派生子类中实现(重写)
    • 在最好的情况下,如果该方法没有在子类中被重写,那么在 编译时间期间将得到一个错误
  • 关于抽象方法,它们是接口定义和基类中实际实现的一部分的混合体。两者同时发生。因为迅速是非常新的和非常干净的定义,它没有这样的方便,但“不干净”的概念(尚未)。
  • 对我来说(一个可怜的老 Java 家伙) ,这个问题不断演变。我已经通读了这篇文章中的所有答案,这次我认为我找到了一种模式,至少在我看来是可行的。
  • 更新 : 看起来苹果的 UIKit 实现者使用了相同的模式。UITableViewController实现了 UITableViewDelegate,但仍需要通过显式设置 delegate属性将寄存器作为委托。
  • 这些都是在 Xcode 7.3.1的 Playground 上测试的