Подтвердить что ты не робот

Абстрактные классы в языке Swift

Есть ли способ создать абстрактный класс на языке Swift, или это ограничение, как Objective-C? Я бы хотел создать абстрактный класс, сопоставимый с тем, что Java определяет как абстрактный класс.

4b9b3361

Ответ 1

В Swift нет абстрактных классов (например, Objective-C). Лучше всего использовать Protocol, который похож на интерфейс Java.

С Swift 2.0 вы можете добавить реализации методов и вычисленные реализации свойств с использованием расширений протокола. Ваши единственные ограничения заключаются в том, что вы не можете предоставить переменные-члены или константы и нет динамической отправки.

Примером этого метода может быть:

protocol Employee {
    var annualSalary: Int {get}
}

extension Employee {
    var biweeklySalary: Int {
        return self.annualSalary / 26
    }

    func logSalary() {
        print("$\(self.annualSalary) per year or $\(self.biweeklySalary) biweekly")
    }
}

struct SoftwareEngineer: Employee {
    var annualSalary: Int

    func logSalary() {
        print("overridden")
    }
}

let sarah = SoftwareEngineer(annualSalary: 100000)
sarah.logSalary() // prints: overridden
(sarah as Employee).logSalary() // prints: $100000 per year or $3846 biweekly

Обратите внимание, что это обеспечивает "абстрактный класс" как функции даже для структур, но классы также могут реализовывать один и тот же протокол.

Также обратите внимание, что каждый класс или структура, реализующая протокол Employee, должен будет снова объявить свойство annualSalary.

Самое главное, обратите внимание, что нет динамической отправки. Когда logSalary вызывается в экземпляре, который хранится как SoftwareEngineer, он вызывает переопределенную версию метода. Когда logSalary вызывается в экземпляре после того, как он был добавлен в Employee, он вызывает исходную реализацию (она не динамически не отправляет переопределенную версию, даже если экземпляр фактически является Software Engineer.

Для получения дополнительной информации просмотрите видео WWDC об этой функции: Создание лучших приложений со значениями типов в Swift

Ответ 2

Обратите внимание, что этот ответ предназначен для Swift 2.0 и выше

Вы можете добиться такого же поведения с протоколами и расширениями протокола.

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

protocol Drivable {
    var speed: Float { get set }
}

Затем вы можете добавить поведение по умолчанию ко всем типам, которые соответствуют ему

extension Drivable {
    func accelerate(by: Float) {
        speed += by
    }
}

Теперь вы можете создавать новые типы, реализуя Drivable.

struct Car: Drivable {
    var speed: Float = 0.0
    init() {}
}

let c = Car()
c.accelerate(10)

Итак, в основном вы получаете:

  • Сроки выполнения компиляции, гарантирующие, что все Drivable реализуют speed
  • Вы можете реализовать поведение по умолчанию для всех типов, соответствующих Drivable (accelerate)
  • Drivable гарантированно не создается, поскольку это просто протокол

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

Ответ 3

Я думаю, что это самый близкий к Java abstract или С# abstract:

class AbstractClass {

    private init() {

    }
}

Обратите внимание, что для того, чтобы модификаторы private работали, вы должны определить этот класс в отдельном файле Swift.

EDIT: Тем не менее, этот код не позволяет объявить абстрактный метод и тем самым принудительно выполнить его реализацию.

Ответ 4

После того, как я боролся несколько недель, я наконец понял, как перевести абстрактный класс Java/PHP в Swift:

public class AbstractClass: NSObject {

    internal override init(){}

    public func getFoodToEat()->String
    {
        if(self._iAmHungry())
        {
            return self._myFavoriteFood();
        }else{
            return "";
        }
    }

    private func _myFavoriteFood()->String
    {
        return "Sandwich";
    }

    internal func _iAmHungry()->Bool
    {
        fatalError(__FUNCTION__ + "Must be overridden");
        return false;
    }
}

public class ConcreteClass: AbstractClass, IConcreteClass {

    private var _hungry: Bool = false;

    public override init() {
        super.init();
    }

    public func starve()->Void
    {
        self._hungry = true;
    }

    public override func _iAmHungry()->Bool
    {
        return self._hungry;
    }
}

public protocol IConcreteClass
{
    func _iAmHungry()->Bool;
}

class ConcreteClassTest: XCTestCase {

    func testExample() {

        var concreteClass: ConcreteClass = ConcreteClass();

        XCTAssertEqual("", concreteClass.getFoodToEat());

        concreteClass.starve();

        XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
    }
}

Однако я думаю, что Apple не реализовала абстрактные классы, потому что вместо этого он обычно использует шаблон делегата + протокола. Например, такой же шаблон выше был бы лучше выполнен следующим образом:

import UIKit

    public class GoldenSpoonChild
    {
        private var delegate: IStomach!;

        internal init(){}

        internal func setup(delegate: IStomach)
        {
            self.delegate = delegate;
        }

        public func getFoodToEat()->String
        {
            if(self.delegate.iAmHungry())
            {
                return self._myFavoriteFood();
            }else{
                return "";
            }
        }

        private func _myFavoriteFood()->String
        {
            return "Sandwich";
        }
    }

    public class Mother: GoldenSpoonChild, IStomach
    {

        private var _hungry: Bool = false;

        public override init()
        {
            super.init();
            super.setup(self);
        }

        public func makeFamilyHungry()->Void
        {
            self._hungry = true;
        }

        public func iAmHungry()->Bool
        {
            return self._hungry;
        }
    }

    protocol IStomach
    {
        func iAmHungry()->Bool;
    }

    class DelegateTest: XCTestCase {

        func testGetFood() {

            var concreteClass: Mother = Mother();

            XCTAssertEqual("", concreteClass.getFoodToEat());

            concreteClass.makeFamilyHungry();

            XCTAssertEqual("Sandwich", concreteClass.getFoodToEat());
        }
    }

Мне понадобился такой шаблон, потому что я хотел использовать некоторые методы в UITableViewController, такие как viewWillAppear и т.д. Было ли это полезно?

Ответ 5

Существует способ моделирования абстрактных классов с использованием протоколов. Это пример:

protocol MyProtocol {
   func doIt()
}

class BaseClass {
    var myDelegate: MyProtocol!

    init() {
        ...
    }

    func myFunc() {
        ...
        self.myDelegate.doIt()
        ...
    }
}

class ChildClass: BaseClass, MyProtocol {
    override init(){
        super.init()
        self.myDelegate = self
    }

    func doIt() {
        // Custom implementation
    }
}

Ответ 6

Самый простой способ - использовать вызов fatalError("Not Implemented") в абстрактном методе (не переменном) в расширении протокола.

protocol MyInterface {
    func myMethod() -> String
}


extension MyInterface {

    func myMethod() -> String {
        fatalError("Not Implemented")
    }

}

class MyConcreteClass: MyInterface {

    func myMethod() -> String {
        return "The output"
    }

}

MyConcreteClass().myMethod()

Ответ 7

Еще один способ реализовать абстрактный класс - блокировать инициализатор. Я сделал это так:

class Element:CALayer { // IT ABSTRACT CLASS

    override init(){ 
        super.init()
        if self.dynamicType === Element.self {
        fatalError("Element is abstract class, do not try to create instance of this class")
        }
    }
}

Ответ 8

Я пытался создать абстрактный класс Weather, но использование протоколов не было идеальным, так как мне приходилось писать одни и те же методы init снова и снова. Расширение протокола и запись метода init возникли, особенно если я использовал NSObject, соответствующий NSCoding.

Итак, я придумал это для соответствия NSCoding:

required init?(coder aDecoder: NSCoder) {
    guard type(of: self) != Weather.self else {
        fatalError("<Weather> This is an abstract class. Use a subclass of `Weather`.")
    }
    // Initialize...
}        

Что касается init:

fileprivate init(param: Any...) {
    // Initialize
}