[Swift] クラスのイニシャライザ5: required 修飾子

More than 1 year has passed since last update.

はじめに

この記事では、required 修飾子について説明します。

他の記事へのリンク

環境

  • Xcode 6.1 (Playground)

required 修飾子とは?

required 修飾子は、以下の2つの状況で使います。

  • 状況1) サブクラスにイニシャライザのオーバーライドを強制するとき
  • 状況2) プロトコルのイニシャライザをオーバーライドするとき

状況1) サブクラスにイニシャライザのオーバーライドを強制するとき

Designated -> Designated の場合

required_d2d.png

class Animal {
    let name: String

    //
    // A(サブクラスにイニシャライザのオーバーライドを強制するときは、required 修飾子を付ける)
    //    
    required init() {
        self.name = "unknown"
    }
}

class Cat: Animal {
    //
    // A をオーバーライド(override 修飾子ではなく、required 修飾子を使うことに注意)
    //
    required init() {
        super.init()
    }
}

サブクラスにイニシャライザのオーバーライドを強制するときは、required 修飾子を使います。

上記コードを一部抜粋
    //
    // A(サブクラスにこのイニシャライザのオーバーライドを強制するときは、required 修飾子を付ける)
    //    
    required init() {
        self.name = "unknown"
    }

また、required 修飾子の付いたスーパクラスのイニシャライザをオーバーライドするときにも、required 修飾子を使います。

この場合には、override 修飾子ではなく、required 修飾子を使うことに注意してください。

上記コードを一部抜粋
    //
    // A をオーバーライド(override 修飾子ではなく、required 修飾子を使うことに注意)
    //
    required init() {
        super.init()
    }

補足1) override 修飾子ではなく、required 修飾子を使う理由

上記で override 修飾子を使えないのは、サブクラスのサブクラスにイニシャライザのオーバーライドを強制するためではないかと思われます。

class Animal {
    let name: String

    //
    // A
    //
    required init() {
        self.name = "unknown"
    }
}

class Cat: Animal {
    //
    // A'(A をオーバーライド。ここに required 修飾子がないと、サブクラス PersianCat に、このイニシャライザのオーバーライドを強制できない)
    //
    required init() {
        super.init()
    }
}

class PersianCat: Cat {
    //
    // A' をオーバーライド
    //
    required init() {
        super.init()
    }
}

補足2) インスタンスメソッドでは、required 修飾子を使用できない

required 修飾子はイニシャライザでしか使えません。インスタンスメソッドでは使用できません。

class Animal {
    required func makeSound() { // -> error: 'required' may only be used on 'init' declarations
    }
}

Designated -> Convenience の場合

required_d2c.png

class Animal {
    let name: String

    //
    // A
    //
    init(name: String) {
        self.name = name
    }

    //
    // B
    //
    required convenience init() {
        self.init(name: "unknown")
    }
}

class Cat: Animal {
    //
    // B をオーバーライド
    //
    required init() {
        //
        // A を呼び出す
        //
        super.init(name: "unknown")
    }
}

Convenience -> Designated の場合

required_c2d.png

class Animal {
    let name: String

    //
    // A
    //
    init() {
        self.name = "unknown"
    }

    //
    // B
    //
    required init(name: String) {
        self.name = name
    }
}

class Cat: Animal {
    //
    // B をオーバーライド
    //
    required convenience init(name: String) {
        //
        // スーパークラスから自動的に継承された A を呼び出す(下記の「注意1」「注意2」を参照)
        //
        self.init()
    }
}

注意1)

self.init() では、スーパークラスから自動的に継承されたイニシャライザを呼び出しています。

詳細は、以下の記事を参照してください。

注意2)

オーバーライドをしている場合でも、convenience initializer の中では、スーパークラスのイニシャライザを呼び出せないことに注意してください。

詳細は、以下の記事を参照してください。

Convenience -> Convenience の場合

required_c2c.png

class Animal {
    let name: String

    //
    // A
    //
    init(name: String) {
        self.name = name
    }

    //
    // B
    //
    required convenience init() {
        self.init(name: "unknown")
    }
}

class Cat: Animal {
    //
    // B をオーバーライド
    //
    required convenience init() {
        //
        // スーパークラスから自動的に継承された A を呼び出す(上記の「注意1」「注意2」を参照)
        //
        self.init(name: "unknown")
    }
}

状況2) プロトコルのイニシャライザをオーバーライドするとき

Designated -> プロトコルのイニシャライザの場合

required_d2protocol.png

protocol SomeProtocol {
    //
    // A
    //
    init()
}

class Animal: SomeProtocol {
    //
    // A をオーバーライド
    //
    required init() {
    }
}

プロトコルのイニシャライザには、required 修飾子は不要です。

上記コードを一部抜粋
protocol SomeProtocol {
    //
    // A
    //
    init()
}

一方で、プロトコルのイニシャライザをオーバーライドするときは、required 修飾子が必要です。

上記コードを一部抜粋
class Animal: SomeProtocol {
    //
    // A をオーバーライド
    //
    required init() {
    }
}

補足) プロトコルでは、Convenience Initializer を宣言できない

プロトコルでは、convenience initializer を宣言できないことに注意してください。

protocol SomeProtocol {
    convenience init() // ->  error: convenience initializer not allowed in non-class type
}

Convenience -> プロトコルのイニシャライザの場合

required_c2protocol.png

protocol SomeProtocol {
    //
    // A
    //
    init(name: String)
}

class Animal: SomeProtocol {
    init() {
    }

    //
    // A をオーバーライド
    //
    required convenience init(name: String) {
        self.init()
    }
}

補足1) final クラスの場合は、required 修飾子はあってもなくてもいい

protocol SomeProtocol {
    //
    // A
    //
    init()
}

final class Animal: SomeProtocol {
    //
    // A をオーバーライド(required 修飾子はあってもなくてもいい)
    //
    init() {
    }
}

final クラスの場合は、required 修飾子はあってもなくてもかまいません(サブクラスにそのイニシャライザのオーバーライドを強制する必要がないため)。

一方で、final クラスでない場合は、required 修飾子が必要です(サブクラスにそのイニシャライザのオーバーライドを強制する必要があるため)。

finalクラスでない場合
protocol SomeProtocol {
    //
    // A
    //
    init()
}

class Animal: SomeProtocol {
    //
    // A'(A をオーバーライド。ここに required 修飾子がないと、サブクラス Cat に、このイニシャライザのオーバーライドを強制できない)
    //
    required init() {
    }
}

class Cat: Animal {
    //
    // A' をオーバーライド
    //
    required init() {
    }
}

補足2) インスタンスメソッドの場合は、required 修飾子は不要

protocol AnimalProtocol {
    //
    // A
    //
    func makeSound()
}

class Cat: AnimalProtocol {
    //
    // A をオーバーライド(required 修飾子は不要)
    //
    func makeSound() {
        println("Mew")
    }
}

補足3) override 修飾子と required 修飾子の両方が必要な場合

スーパークラスとプロトコルのイニシャライザを同時にオーバーライドする場合は、override 修飾子と required 修飾子の両方を付ける必要があります。

protocol SomeProtocol {
    init()
}

class Animal {
    init() {
    }
}

class Cat: Animal, SomeProtocol {
    //
    // 1. プロトコルのイニシャライザをオーバーライドしているので、required 修飾子が必要
    // 2. スーパークラスのイニシャライザもオーバーライドしているので、override 修飾子も必要
    //
    required override init() {
    }
}

override 修飾子と required 修飾子は逆でもかまいません。

上記コードを一部抜粋の上、一部改変
class Cat: Animal, SomeProtocol {
    //
    // override 修飾子と required 修飾子は逆でもかまわない
    //
    override required init() {
    }
}

参考文献

The Swift Programming Language

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.