web-dev-qa-db-ja.com

Swiftクラスで必要な初期化子を使用する理由は?

Swiftクラスでのrequiredキーワードの使用を理解しようとしています。

class SomeClass 
{
    required init() {
        // initializer implementation goes here
    }
}

requiredは、子クラスにメソッドを実装することを強制しません。親クラスのrequired指定初期化子をオーバーライドする場合は、requiredではなくoverrideを記述する必要があります。私はそれがどのように機能するかを知っていますが、なぜこれをするべきか理解できません。

requiredの利点は何ですか?私が知る限り、C#のような言語にはこのようなものはなく、overrideで問題なく動作します。

48
TalkingCode

実際には、このクラスがサブクラスを持っている場合、それらがこの同じ初期化子を継承または実装することを保証するためにコンパイラを満足させる方法にすぎません。 サブクラスがそれ自体の指定された初期化子を持っている場合、スーパークラスの初期化子は継承されないというルールのため、この点には疑問があります。したがって、スーパークラスが初期化子を持ち、サブクラスnotを持つことが可能です。 requiredはその可能性を克服します。

コンパイラがこの方法で満たされる必要がある1つの状況は、プロトコルに関係し、次のように機能します。

_protocol Flier {
    init()
}
class Bird: Flier {
    init() {} // compile error
}
_

問題は、Birdにサブクラスがある場合、そのサブクラスはinitを実装または継承する必要があり、それを保証していないことです。 Birdのinitrequiredとしてマークすると、保証されます。

別の方法として、Birdをfinalとしてマークし、逆を保証します。つまり、サブクラスを持たないようにします。

別の状況では、同じ初期化子を呼び出すことでクラスまたはそのサブクラスを作成できるファクトリメソッドがあります。

_class Dog {
    var name: String
    init(name: String) {
        self.name = name
    }
}

class NoisyDog: Dog {

}

func dogMakerAndNamer(whattype: Dog.Type) -> Dog {
    let d = whattype.init(name: "Fido") // compile error
    return d
}
_

dogMakerAndNamerは、DogまたはDogサブクラスでinit(name:)初期化子を呼び出しています。しかし、サブクラスにinit(name:)初期化子があることをコンパイラーがどのように確認できますか? requiredの指定は、コンパイラーの不安を和らげます。

80
matt

Mattが上記で与えたものとは別に、Requiredによって提供される別のソリューションに注意を喚起したいと思います。

_class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"

}
let instanceSubClass = subClass()
instanceSubClass.name        //output: "Untitled"
instanceSubClass.neakName    //output: "Subclass Untitled"
_

上記の例で確認できるように、superClassrequired init()を宣言し、subClassでsuperClassのinit()初期化子がデフォルトで継承しているので、subClass let instanceSubClass = subClass()

ただし、サブクラスに指定された初期化子を1つ追加して、実行時の値をストアドプロパティneakNameに割り当てるとします。もちろん追加することはできますが、その結果、superClassの初期化子はsubClassに継承されません。したがって、subClassのインスタンスを作成する場合は、以下のように、独自の指定イニシャライザーを使用して作成します。

_class superClass{
    var name: String
    init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"
_

上記では、subClass()だけではsubClassのインスタンスを作成できません。ただし、ただし、superClassのすべてのサブクラスに独自のinit()初期化子が必要な場合subClass()によって直接インスタンスを作成します。スーパークラスのinit()の前にrequiredキーワードを置くだけで、subClassにもinit()初期化子を追加するよう強制されます-以下のように。

_class superClass{
    var name: String
    required init(){
        // initializer implementation goes here
        self.name = "Untitled"
    }
}
class subClass: superClass {
    var neakName: String = "Subclass Untitled"
    init(neakName: String) {
        self.neakName = neakName
    }
}    // Compiler error <------------ required `init()` must be provided by subClass.
let instanceSubClass = subClass(neakName: "Bobby")
instanceSubClass.name       //output: "Untitled"
instanceSubClass.neakName   //output: "Bobby"  
_

SO、すべてのサブクラスをスーパークラスの_required initializer_で実装する必要がある場合、スーパークラスの初期化子の前にrequiredキーワードを使用します。

7
Kiran Jasvanee

ドキュメント によると:

Write the required modifier before the definition of a class initializer to
indicate that every subclass of the class must implement that initializer

はい、必須です。すべての子クラスにこのコンストラクターを強制的に実装させます。ただし、これは必要ありません

 if you can satisfy the requirement with an inherited initializer.

したがって、親コンストラクタで完全に初期化できないより複雑なクラスを作成した場合、requireコンストラクタを実装する必要があります。

ドキュメントからの例(いくつか追加されたものを含む):

class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

class SomeSubclass: SomeClass {
    let thisNeedsToBeInitialized: String
    required init() {
        // subclass implementation of the required initializer goes here
        self.thisNeedsToBeInitialized = "default value"
    }
}
5
pseudonym117

サブクラスに独自の初期化子を追加しようとする場合、スーパークラスで宣言された特定の事項に従う必要があります。そのため、必要なメソッドを実装することを忘れないようにしてください。コンパイラを忘れると、エラー// fatal error, we've not included the required init()が表示されます。もう1つの理由は、サブクラスが独自の初期化子を定義しているときにサブクラスが従う必要がある一連の条件を作成することです。

0
Amit89