web-dev-qa-db-ja.com

Swift言語の抽象クラス

Swift言語で抽象クラスを作成する方法はありますか、またはこれはObjective-Cのような制限ですか? Javaが抽象クラスとして定義するものに匹敵する抽象クラスを作成したいと思います。

132
kev

Swiftには抽象クラスはありません(Objective-Cと同様)。あなたの最善の策は、Javaインターフェイスのような Protocol を使用することです。

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プロトコルを実装するすべてのクラスまたは構造体は、annualSalaryプロパティを再度宣言する必要があることに注意してください。

最も重要なことは、動的ディスパッチがないことですlogSalaryとして保存されているインスタンスでSoftwareEngineerが呼び出されると、オーバーライドされたバージョンのメソッドが呼び出されます。 logSalaryにキャストされた後にインスタンスでEmployeeが呼び出されると、元の実装が呼び出されます(インスタンスが実際にSoftware Engineerであっても、オーバーライドされたバージョンに動的にディスパッチされません)。

詳細については、その機能に関する優れたWWDCビデオを確認してください。 Swiftで値型を使用したより良いアプリの構築

161
drewag

この回答は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. Drivableaccelerate)に準拠するすべてのタイプにdefault-behaviourを実装できます
  3. Drivableは単なるプロトコルであるため、インスタンス化されないことが保証されています

このモデルは実際には特性のように動作します。つまり、複数のプロトコルに準拠し、それらのいずれかのデフォルト実装を使用できますが、抽象スーパークラスでは単純なクラス階層に制限されます。

47
NSAddict

これはJavaのabstractまたはC#のabstractに最も近いと思います。

class AbstractClass {

    private init() {

    }
}

private修飾子が機能するためには、このクラスを別のSwiftファイルで定義する必要があることに注意してください。

EDIT:それでも、このコードでは抽象メソッドを宣言できないため、その実装を強制します。

14
Teejay

数週間苦労した後、私はついに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は抽象クラスを実装しなかったと思います。これは、通常、代わりにデリゲート+プロトコルパターンを使用するためです。たとえば、上記の同じパターンは、次のように実行する方が適切です。

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

ViewWillAppearなどのUITableViewControllerの一部のメソッドを共通化するため、この種のパターンが必要でした。これは役に立ちましたか?

9
Josh Woodcock

最も簡単な方法は、プロトコル拡張の抽象メソッド(変数ではない)に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()
8
Carlos García

プロトコルを使用して抽象クラスをシミュレートする方法があります。これは一例です:

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
    }
}
6
David Seca

抽象クラスを実装するもう1つの方法は、初期化子をブロックすることです。私はこの方法でそれをやった:

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

基本クラスの抽象プロパティおよびメソッドへのすべての参照をプロトコル拡張実装に移動します。ここで、自己制約は基本クラスになります。基本クラスのすべてのメソッドとプロパティにアクセスできます。さらに、派生クラスのプロトコルで抽象メソッドとプロパティのコンパイラチェック実装

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

動的ディスパッチがないという制限により、次のようなことができます。

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()
0
Christo Smal

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
}
0
funct7