快速语言中的抽象类

有没有一种方法可以在 Swift 语言中创建一个抽象类,或者这是一个类似 Objective-C 的限制?我想创建一个抽象类,类似于 Java 定义的抽象类。

101945 次浏览

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

请注意,即使对于结构,这也提供了类似于“抽象类”的特性,但是类也可以实现相同的协议。

还要注意的是,实现 Employee 协议的每个类或结构都必须再次声明 year Salary 属性。

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

要了解更多信息,请查看关于该特性的伟大的 WWDC 视频: 在 Swift 中使用价值类型构建更好的应用程序

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

class AbstractClass {


private init() {


}
}

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

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

经过几个星期的努力,我终于意识到如何将一个 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());
}
}

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

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 等。这有帮助吗?

请注意,这个答案是针对 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. 编译时间检查,以保证所有 Drivable都实现 speed
  2. 您可以为所有符合 Drivable(accelerate)的类型实现默认行为
  3. Drivable保证不会被实例化,因为它只是一个协议

这个模型的行为实际上更像 trait,这意味着您可以遵循多个协议并采用其中任何一个的默认实现,而对于抽象超类,您仅限于一个简单的类层次结构。

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

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

最简单的方法是在协议扩展上使用对 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 {
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
}
}

我试图创建一个 Weather抽象类,但是使用协议并不理想,因为我必须一遍又一遍地编写相同的 init方法。扩展协议和编写 init方法有它的问题,特别是因为我使用的是符合 NSCodingNSObject

所以我想出了这个 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
}

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

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

在没有克劳斯·福尔曼的限制下,你可以这样做:

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()

这是一个非常老的问题,但仍然... ... 下面是在 Swift 5.2上编译的实际代码片段,它可以按预期的方式工作:

protocol Context {
init() throws
func out(_ aStr: String) throws
// Other stuff
}


class AbstractContext: Context {
required init() throws {
if Self.self === AbstractContext.self {
preconditionFailure("Call to abstract method \(Self.self).\(#function)")
}
}


func out(_ aStr: String) throws {
preconditionFailure("Call to abstract method \(Self.self).\(#function)")
}


// Other stuff
}


class CompileContext: AbstractContext {
required init() throws {}


override func out(_ aStr: String) throws {
print(aStr)
}


// Other stuff
}

这是我移除 CompileContext.out后得到的结果:

Fatal error: Call to abstract method CompileContext.out(_:): file swiftpg/contexts.swift, line 28