Абстрактные функции в языке Swift

Я хотел бы создать абстрактную функцию на языке быстрой речи. Возможно ли это?

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

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

Ответ 1

В Swift нет понятия абстрактного (например, Objective-C), но вы можете сделать это:

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

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

Ответ 2

Что вы хотите, это не базовый класс, а протокол.

protocol MyProtocol {
    func abstractFunction()
}

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

Если вы не предоставляете abstractFunction в своем классе, это ошибка.

Если вам по-прежнему нужен базовый класс для другого поведения, вы можете сделать это:

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

Ответ 3

Я переношу довольно много кода с платформы, которая поддерживает абстрактные базовые классы для 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, чтобы базовый класс имел экземпляр протокола, с которым он мог выполнить свою работу.

Например, допустим, что sharedFunction необходимо вызвать abstractFunction. Протокол останется прежним, и теперь классы будут выглядеть так:

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 из производного класса удовлетворяет той части протокола, но производный класс по-прежнему способен делиться логикой базового класса достаточно простым способом.

Ответ 4

Этот, кажется, "официальный" способ использования 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()

Обратитесь к игровой площадке, чтобы увидеть результат.

Некоторые замечания

  • Сначала было дано много ответов. Я надеюсь, что кто-то найдет все, вплоть до этого.
  • Вопрос на самом деле заключался в том, чтобы найти шаблон, который реализует:
    • Класс вызывает метод, который должен быть реализован в одном из его производных подклассов (переопределенных)
    • В лучшем случае, если метод не переопределен в подклассе, получите ошибку во время времени компиляции
  • Дело в абстрактных методах заключается в том, что они представляют собой смесь определения интерфейса и части фактической реализации в базовом классе. Оба в то же время. Поскольку быстрый очень новый и очень чистый, он не имеет такой уверенности, но "нечистые" концепции (пока).
  • Для меня (плохой старый Java-парень) эта проблема время от времени развивается. Я прочитал все ответы в этом сообщении, и на этот раз я думаю, что нашел шаблон, который выглядит выполнимым - по крайней мере для меня.
  • Обновление. Похоже, что разработчики UIKit в Apple используют один и тот же шаблон. UITableViewController реализует UITableViewDelegate, но все равно должен быть зарегистрирован как делегат, явно устанавливая свойство delegate.
  • Все это протестировано на игровой площадке Xcode 7.3.1.

Ответ 5

Один из способов сделать это - использовать необязательное закрытие, определенное в базовом классе, и дети могут его реализовать или нет.

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

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

Ответ 6

Я понимаю, что вы сейчас делаете, я думаю, вам будет лучше использовать протокол

protocol BaseProtocol {
    func abstractFunction()
}

Затем вы просто соглашаетесь с протоколом:

class SubClass : BaseProtocol {

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

Если ваш класс также является подклассом, протоколы следуют над суперклассом:

class SubClass: SuperClass, ProtocolOne, ProtocolTwo {}

Ответ 7

Использование ключевого слова 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

Однако, поскольку Стив Waddicor сказал, что вы, скорее всего, захотите protocol.

Ответ 8

Хорошо, я знаю, что я опаздываю на игру и что могу воспользоваться теми изменениями, которые произошли. Извините.

В любом случае, я хотел бы внести свой ответ, потому что мне нравится делать тестирование, а решения с 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() или исключений, которые произойдут во время выполнения.

Ответ 9

Я не знаю, будет ли это полезно, но у меня была аналогичная проблема с абстрактным методом при попытке создать игру SpritKit. То, что я хотел, является абстрактным классом Animal, который имеет такие методы, как move(), run() и т.д., Но имена спрайтов (и другие функции) должны предоставляться детьми класса. Итак, я закончил делать что-то вроде этого (проверено на 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"]
        }
    }
}

Ответ 10

Я понимаю этот вопрос и искал одно и то же решение. Протоколы не совпадают с абстрактными методами.

В протоколе необходимо указать, что ваш класс соответствует такому протоколу, абстрактный метод означает, что вы должны переопределить такой метод.

Другими словами, протоколы являются опциями, вам нужно указать базовый класс и протокол, если вы не укажете протокол, тогда вам не нужно переопределять такие методы.

Абстрактный метод означает, что вы хотите базовый класс, но вам нужно реализовать свой собственный метод или два, что не то же самое.

Мне нужно такое же поведение, поэтому я искал решение. Я считаю, что Swift отсутствует такая функция.

Ответ 11

Есть еще одна альтернатива этой проблеме, хотя по сравнению с предложением @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();
    }
}

Все, что приходит после этого, недоступно, поэтому я не понимаю, зачем принудительно возвращать.