Swift语言中的抽象类

kev*_*kev 136 inheritance abstract-class subclass swift

有没有办法在Swift语言中创建一个抽象类,或者这是一个像Objective-C一样的限制?我想创建一个类似于Java定义为抽象类的抽象类.

dre*_*wag 168

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: $100000 per year or $3846 biweekly
Run Code Online (Sandbox Code Playgroud)

请注意,即使对于结构体,这也提供了类似于"抽象类"的功能,但类也可以实现相同的协议.

另请注意,实现Employee协议的每个类或结构都必须再次声明annualSalary属性.

最重要的是,请注意没有动态调度.当logSalary在存储为实例的实例上SoftwareEngineer调用时,它会调用该方法的重写版本.当logSalary被调用的情况下,已强制转换为后Employee,它会调用原始的实现(它不没有动态分派到重写版本,即使该实例实际上是一个Software Engineer.

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

  • `protocol Animal {var property:Int {get set}}`.如果您不希望酒店拥有一台二传手,您也可以省略该套餐 (3认同)
  • 我认为[这个wwdc视频](https://developer.apple.com/videos/wwdc/2015/?id=408)更具相关性 (3认同)
  • 如果只是将`func logSalary()`添加到Employee协议声明中,该示例将为两次调用`logSalary()`输出`overridden`.这是在Swift 3.1中.因此,您可以获得多态性的好处.在这两种情况下都会调用正确的方法. (3认同)
  • @MarioZannone那段视频让我大吃一惊,让我爱上了Swift. (2认同)

NSA*_*ict 47

请注意,此答案针对的是Swift 2.0及更高版本

您可以使用协议和协议扩展来实现相同的行为.

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

protocol Drivable {
    var speed: Float { get set }
}
Run Code Online (Sandbox Code Playgroud)

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

extension Drivable {
    func accelerate(by: Float) {
        speed += by
    }
}
Run Code Online (Sandbox Code Playgroud)

您现在可以通过实现创建新类型Drivable.

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

let c = Car()
c.accelerate(10)
Run Code Online (Sandbox Code Playgroud)

所以基本上你得到:

  1. 编译时间检查,保证所有Drivable的实现speed
  2. 您可以为符合Drivable(accelerate)的所有类型实现默认行为
  3. Drivable 保证不会被实例化,因为它只是一个协议

这个模型实际上更像是traits,这意味着你可以符合多个协议并采用其中任何一个的默认实现,而对于一个抽象的超类,你只能局限于一个简单的类层次结构.

  • 您不能在 ˚Car˚ 中覆盖 ˚accelerate˚。如果这样做, ˚extentsion Driveable˚ 中的实现仍会在没有任何编译器警告的情况下被调用。非常不同于 Java 抽象类 (2认同)

Tee*_*jay 14

我认为这是最接近Java abstract或C#的abstract:

class AbstractClass {

    private init() {

    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,为了使private修饰符起作用,必须在单独的Swift文件中定义此类.

编辑:仍然,此代码不允许声明一个抽象方法,从而强制其实现.

  • 尽管如此,这并不强制*子类覆盖一个函数,同时在父类中也有一个该函数的基本实现. (4认同)
  • ConcreteClass应该有一个公共构造函数.您可能需要AbstractClass中的受保护构造函数,除非它们位于同一文件中.根据我的记忆,Swift中不存在***protected***访问修饰符.所以解决方案是在同一个文件中声明ConcreteClass. (2认同)

Jos*_*ock 10

经过几周的努力,我终于意识到如何将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());
    }
}
Run Code Online (Sandbox Code Playgroud)

但是我认为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());
        }
    }
Run Code Online (Sandbox Code Playgroud)

我需要这种模式,因为我想在UITableViewController中使用一些方法,例如viewWillAppear等.这有用吗?


小智 9

最简单的方法是使用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()
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的答案。我不认为如果你调用 `(MyConcreteClass() as MyInterface).myMethod()` 会起作用,但它确实有效!关键是在协议声明中包含`myMethod`;否则调用会崩溃。 (2认同)

Dav*_*eca 7

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

protocol MyProtocol {
   func doIt()
}

class BaseClass {
    weak var myDelegate: MyProtocol?

    init() {
        ...
    }

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

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

    func doIt() {
        // Custom implementation
    }
}
Run Code Online (Sandbox Code Playgroud)