有没有一种方法可以在Swift语言中创建抽象类,或者这只是像Objective-C一样的限制?我想创建一个抽象类,类似于Java定义的抽象类。
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
注意,即使是结构体,它也提供了 "抽象类" 类似的功能,但类也可以实现相同的协议。protocol Animal { var property: Int { get } }
如果不想让属性有 setter,也可以省略掉 set
。 - drewagEmployee
协议声明中添加func logSalary()
,则示例将为对logSalary()
的两个调用均打印“overridden”。这是在Swift 3.1中实现多态性的好处。在这两种情况下都会调用正确的方法。 - Mike Taverne请注意,此答案针对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)
基本上,您将获得:
Drivable
都实现了speed
Drivable
的所有类型实现默认行为(accelerate
)Drivable
保证不会被实例化,因为它只是一个协议这个模型实际上更像是特征(traits),意味着您可以符合多个协议并且采用其中任何一个的默认实现,而使用抽象超类则仅限于简单的类层次结构。
UICollectionViewDatasource
。我想消除所有样板代码并将其封装在单独的协议/扩展中,然后由多个类重复使用。事实上,在这里模板模式会非常完美,但是... - Richard Topchii最简单的方法是在协议扩展中的抽象方法(不是变量)中调用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()
(MyConcreteClass() as MyInterface).myMethod()
它会起作用,但它确实可以!关键是在协议声明中包含 myMethod
;否则调用会崩溃。 - Mike Taverne我认为这是最接近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等)通用化。 这对您有帮助吗?
使用协议可以模拟抽象类的方式,这是一个例子:
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
}
}
另一种实现抽象类的方法是使用块初始化器。我是这样做的:
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")
}
}
}
这是一个非常老的问题,但仍然存在... 这是一段实际代码片段,可以在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
由于没有动态分派的限制,您可以像这样做:
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()
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;
}
}