Swift-サブクラスでオーバーライドする必要があるクラスメソッド


91

つまり、Swiftで「純粋な仮想関数」を作成する標準的な方法はありますか。その一つすべてのサブクラスによってオーバーライドされなければならないあり、オーバーライドされていない場合は、コンパイル時にエラーが発生します。


これをスーパークラスに実装して、アサーションを作成できます。これがObj-C、Java、Pythonで使用されているのを見てきました。
David Skrundz 2014年

8
@NSArrayこれにより、コンパイル時ではなく実行時エラーが発生します
JuJoDi

この答えはあなたにも役立ちます。ここにリンクの説明を入力
Chamath Jeevan

純粋仮想関数は、次のように実装されているprotocol(と比べてS interfaceあなたが抽象メソッドのようにそれらを使用する必要がある場合は、Javaでの)この質問/答えを見てきた:stackoverflow.com/a/39038828/2435872
jboi

回答:


147

次の2つのオプションがあります。

1.プロトコルを使用する

スーパークラスをクラスではなくプロトコルとして定義する

Pro:各「サブクラス」(実際のサブクラスではない)が必要なメソッドを実装しているかどうかのコンパイル時間チェック

コン:「スーパークラス」(プロトコル)はメソッドやプロパティを実装できません

2.メソッドのスーパーバージョンでアサートする

例:

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

プロ:スーパークラスでメソッドとプロパティを実装できます

短所:コンパイル時のチェックなし


3
@jewirthでも、サブクラスのコンパイル時間チェックは行われません
drewag '

5
プロトコルはメソッドを実装できませんが、代わりに拡張メソッドを介してそれらを提供できます。
David Moles 2015年

2
Swift 2.0以降、プロトコル拡張機能も追加されました:) Apple Reference
Ephemera

4
一方でfatalError、コンパイル時のチェックを提供していない、それはコンパイラが少なくともスマート十分でメソッドの実行パスの呼び出しの戻り値を提供する必要がないであることがいいですfatalError
Bugloaf

3
ケース2:super.someFunc()オーバーライドされたメソッドから呼び出した場合、オーバーライドしたにもかかわらずエラーが発生することに注意してください。あなたはそれを呼び出すことを想定していないことを知っていますが、他の誰かがそれを知っている必要はなく、標準的な慣習に従うだけです。
JakubTruhlář2018

48

以下は、クラスからの継承と、プロトコルのコンパイル時チェックを可能にします:)

protocol ViewControllerProtocol {
    func setupViews()
    func setupConstraints()
}

typealias ViewController = ViewControllerClass & ViewControllerProtocol

class ViewControllerClass : UIViewController {

    override func viewDidLoad() {
        self.setup()
    }

    func setup() {
        guard let controller = self as? ViewController else {
            return
        }

        controller.setupViews()
        controller.setupConstraints()
    }

    //.... and implement methods related to UIViewController at will

}

class SubClass : ViewController {

    //-- in case these aren't here... an error will be presented
    func setupViews() { ... }
    func setupConstraints() { ... }

}

2
ナイス、typealiasが助けに:)
Chris Allinson

このAPIのユーザーがViewControllerからではなくViewControllerClassからclildクラスを派生できないようにする方法はありますか?これは数年後に自分の型エイリアスから派生し、それまでにオーバーライドする必要のある関数について忘れているため、これは私にとって素晴らしい解決策です。
David Rector、

@David Rector、クラスをプライベートに、タイプエイリアスをパブリックにできますか?申し訳ありませんが、私の電話からのメッセージです。自分を確認できません。
ScottyBlades

1
完璧な解決策、ありがとうございます。@DavidRectorによって下線が引かれているように、それを作成するための解決策があり、typealiasのみがpublicだったとしたらすばらしいでしょうが、残念ながらそれは可能ではないようです。
Cyber​​Dandy

35

抽象クラス/仮想関数はサポートされていませんが、ほとんどの場合、プロトコルを使用できます。

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

SomeClassがsomeMethodを実装していない場合、次のコンパイル時エラーが発生します。

error: type 'SomeClass' does not conform to protocol 'SomeProtocol'

30
これは、プロトコルを実装する最上位のクラスでのみ機能することに注意してください。すべてのサブクラスは、プロトコル要件を簡単に無視できます。
memmons 2015年

2
また、プロトコルでのジェネリックの使用はサポートされていません=(
Dielson Sales

14

「仮想」メソッドが多すぎない場合の別の回避策は、サブクラスに「実装」を関数オブジェクトとして基本クラスコンストラクターに渡させることです。

class MyVirtual {

    // 'Implementation' provided by subclass
    let fooImpl: (() -> String)

    // Delegates to 'implementation' provided by subclass
    func foo() -> String {
        return fooImpl()
    }

    init(fooImpl: (() -> String)) {
        self.fooImpl = fooImpl
    }
}

class MyImpl: MyVirtual {

    // 'Implementation' for super.foo()
    func myFoo() -> String {
        return "I am foo"
    }

    init() {
        // pass the 'implementation' to the superclass
        super.init(myFoo)
    }
}

1
いくつかの仮想メソッドがある場合はそれほど役に立ちません
Bushra Shahid 2015

@ xs2bushメソッドの多くが仮想である場合は、プロトコルでそれらを宣言し、拡張メソッドを介して「非仮想」メソッドを提供する方がよいでしょう。
David Moles 2015年

1
それがまさに私がやったこと
Bushra Shahid

0

答えで提案されているようにあなたはアサーション対プロトコルを使用することができ、ここdrewag。ただし、プロトコルの例はありません。ここでカバーします

プロトコル

protocol SomeProtocol {
    func someMethod()
}

class SomeClass: SomeProtocol {
    func someMethod() {}
}

現在、すべてのサブクラスは、コンパイル時にチェックされるプロトコルを実装する必要があります。SomeClassがsomeMethodを実装していない場合、次のコンパイル時エラーが発生します。

エラー:タイプ「SomeClass」はプロトコル「SomeProtocol」に準拠していません

注:これは、プロトコルを実装する最上位のクラスでのみ機能します。すべてのサブクラスは、プロトコル要件を簡単に無視できます。– によるコメントmemmons

アサーション

class SuperClass {
    func someFunc() {
        fatalError("Must Override")
    }
}

class Subclass : SuperClass {
    override func someFunc() {
    }
}

ただし、アサーションは実行時にのみ機能します。


-2

iOS開発の初心者なので、いつ実装されたのかは完全にはわかりませんが、両方の利点を最大限に活用する1つの方法は、プロトコルの拡張機能を実装することです。

protocol ThingsToDo {
    func doThingOne()
}

extension ThingsToDo {
    func doThingTwo() { /* Define code here */}
}

class Person: ThingsToDo {
    func doThingOne() {
        // Already defined in extension
        doThingTwo()
        // Rest of code
    }
}

拡張機能により、通常のプロトコルの関数が定義されていない場合でもコンパイル時エラーが発生する一方で、関数のデフォルト値を使用できます。


1
抽象関数は、デフォルトの実装の反対です
Hogdotmac
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.