关于继承:Swift语言中的抽象类

Abstract classes in Swift Language

有没有一种方法可以用Swift语言创建抽象类,或者这是一个像Objective-C一样的限制?我想创建一个与Java定义为抽象类的抽象类。


在swift中没有抽象类(就像objective-c)。最好的办法是使用一个协议,它就像一个Java接口。

使用Swift2.0,您可以使用协议扩展添加方法实现和计算属性实现。您唯一的限制是不能提供成员变量或常量,并且没有动态调度。

这种技术的一个例子是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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协议的每个类或结构都必须再次声明annualslary属性。

最重要的是,请注意没有动态调度。当对存储为SoftwareEngineer的实例调用logSalary时,它调用方法的重写版本。当logSalary在被强制转换到Employee之后被调用时,它调用原始实现(即使该实例实际上是Software Engineer也不会动态地调度到被重写的版本)。

有关更多信息,请查看有关该功能的精彩WWDC视频:使用swift中的值类型构建更好的应用程序


请注意,此答案针对的是Swift 2.0及以上版本。

您可以通过协议和协议扩展实现相同的行为。

首先,编写一个协议,作为所有必须在所有符合该协议的类型中实现的方法的接口。

1
2
3
protocol Drivable {
    var speed: Float { get set }
}

然后,您可以将默认行为添加到所有符合它的类型中。

1
2
3
4
5
extension Drivable {
    func accelerate(by: Float) {
        speed += by
    }
}

现在,您可以通过实现Drivable来创建新类型。

1
2
3
4
5
6
7
struct Car: Drivable {
    var speed: Float = 0.0
    init() {}
}

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

所以基本上你会得到:

  • 确保所有Drivable执行speed的编译时检查
  • 您可以为符合Drivable(accelerate的所有类型实现缺省行为。
  • Drivable保证不会被实例化,因为它只是一个协议。
  • 这个模型实际上的行为更像特征,这意味着你可以遵守多个协议并采用其中任何一个协议的默认实现,而对于一个抽象的超类,你只能使用一个简单的类层次结构。


    我认为这是最接近Java的EDCOX1的5或C的EDOCX1的5个方面:

    1
    2
    3
    4
    5
    6
    class AbstractClass {

        private init() {

        }
    }

    注意,为了使private修饰符起作用,必须在单独的swift文件中定义这个类。

    编辑:尽管如此,此代码不允许声明抽象方法,从而强制实现它。


    最简单的方法是在协议扩展上使用对fatalError("Not Implemented")的抽象方法(不是变量)的调用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    protocol MyInterface {
        func myMethod() -> String
    }


    extension MyInterface {

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

    }

    class MyConcreteClass: MyInterface {

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

    }

    MyConcreteClass().myMethod()


    在我挣扎了几个星期之后,我终于意识到如何将Java/PHP抽象类翻译成SWIFT:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    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());
        }
    }

    然而,我认为苹果没有实现抽象类,因为它通常使用委托+协议模式。例如,上面的相同模式最好这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    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等。这有帮助吗?


    有一种方法可以使用协议模拟抽象类。这是一个例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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
        }
    }


    实现抽象类的另一种方法是阻止初始值设定项。我是这样做的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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")
            }
        }
    }


    由于没有动态调度的限制,您可以这样做:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    import Foundation

    protocol foo {

        static var instance: foo? { get }
        func prt()

    }

    extension foo {

        func prt() {
            if Thread.callStackSymbols.count > 30 {
                print("super")
            } else {
                Self.instance?.prt()
            }
        }

    }

    class foo1 : foo {

        static var instance : foo? = nil

        init() {
            foo1.instance = self
        }

        func prt() {
            print("foo1")
        }

    }

    class foo2 : foo {

        static var instance : foo? = nil

        init() {
            foo2.instance = self
        }

        func prt() {
            print("foo2")
        }

    }

    class foo3 : foo {

        static var instance : foo? = nil

        init() {
            foo3.instance = self
        }

    }

    var f1 : foo = foo1()
    f1.prt()
    var f2 : foo = foo2()
    f2.prt()
    var f3 : foo = foo3()
    f3.prt()

    将对基类的抽象属性和方法的所有引用移动到协议扩展实现,其中自约束到基类。您将获得对基类的所有方法和属性的访问权。另外,编译器检查派生类协议中抽象方法和属性的实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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;
      }
    }

    我试图创建一个Weather抽象类,但是使用协议并不理想,因为我不得不反复编写相同的init方法。扩展协议和编写init方法有它的问题,特别是因为我使用的NSObject符合NSCoding的要求。

    因此,我为NSCoding的一致性提出了这一点:

    1
    2
    3
    4
    5
    6
    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

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