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


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

9 122

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# ' s 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 не реализовала абстрактные классы, потому что она обычно использует шаблон протокола delegate+. Например, тот же шаблон выше было бы лучше сделать так:

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
}

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

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