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

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

9 ответов


в Swift нет абстрактных классов (так же, как Objective-C). Ваш лучший выбор будет использовать протокол, что похоже на интерфейс 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: 0000 per year or 46 biweekly

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

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

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

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


обратите внимание, что этот ответ нацелен на 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)

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

  1. время компиляции проверяет, что гарантирует, что все Drivables реализовать speed
  2. вы можете реализовать поведение по умолчанию для всех типов, которые соответствуют Drivable (accelerate)
  3. Drivable гарантированно не будет создан экземпляр, так как это просто протокол

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


Я думаю, что это ближе всего к Java abstract или C#'ы abstract:

class AbstractClass {

    private init() {

    }
}

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

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


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


самый простой способ-использовать вызов 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()

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

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
    }
}

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

class Element:CALayer { // IT'S 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")
        }
    }
}

Я пытался сделать 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
}

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

protocol Commom:class{
  var tableView:UITableView {get};
  func update();
}

class Base{
   var total:Int = 0;
}

extension Common where Self:Base{
   func update(){
     total += 1;
     tableView.reloadData();
   }
} 

class Derived:Base,Common{
  var tableView:UITableView{
    return owner.tableView;
  }
}