102
55

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swiftのfinalについて

Last updated at Posted at 2017-08-11

概要

**class定義の前に"final"ってついているけど、どうしてこういう風に書いてるの?**って聞かれたので、自分でも少し曖昧だった部分を調べてまとめました。

finalってなに

  • クラスにfinal修飾子をつけると、継承されるのを禁止できます
  • メソッド、プロパティ、サブスクリプトにfinal修飾子をつけると、サブクラスでオーバーライド(上書き)されるのを禁止できます

finalの意味の通り、クラスであれば、このクラス以降、継承されることがない最後のクラスというのを示します。

final修飾子の例
class Animal { }
final class Dog: Animal { }
class Chihuahua: Dog { } // Dogのサブクラスは作成できない

finalをオーバーライドしようとするとどうなるか

サブクラスでfinalのついたメソッド、プロパティ、サブスクリプトをオーバライドしようとすると、コンパイルエラーになります。

// プロパティ、メソッド、サブスクリプトにfinalがついているクラス
class SomeClass {
    final var someProperty: Int = 0
    
    final func someMethod() {
        
    }
    
    final subscript(index: Int) -> Int {
        return index * 2
    }
}

// SomeClassを継承したサブクラス
class SomeSubClass: SomeClass {
    // プロパティがオーバーライドできないのでコンパイルエラー 
    // error: var overrides a 'final' var
    override var someProperty: Int {
        get {
            return super.someProperty + 1
        }
        set {
            super.someProperty = newValue
        }
    }
    
    // メソッドがオーバーライドできないのでコンパイルエラー
    // error: instance method overrides a 'final' instance method
    override func someMethod() {
        
    }
    
    // サブスクリプトがオーバーライドできないのでコンパ入りエラー
    // error: subscript overrides a 'final' subscript
    override subscript(index: Int) -> Int {
        return super[index] + 1
    }
}

また同様に、finalのついたクラスを継承したサブクラスを作ろうとすると、継承自体ができなくなり、コンパイルエラーとなります。

finalがついたclassを継承しようとした場合
final class SomeClass {
    
}

// SomeClassを継承したサブクラス
class SomeSubClass: SomeClass {
    // コンパイルエラー error: inheritance from a final class
}

どういうときにfinalをつけるか

継承の必要性がないときにはfinalをつけるのが良いと思います。
逆にサブクラスを作成する必要がでてきたらfinalを外します。

finalをつけるメリット

  • 動的ディスパッチ(後述)の可能性を減らし、プログラムの実行時のパフォーマンスが改善できる
  • サブクラスを経由してセキュリティ対策等必ず実行したい処理を回避されることを防ぐ
  • サブクラスを作成することにより、設計が崩れることを防ぐ
  • 継承・オーバーライドされることがないものとしてプログラムの理解に役立つ

動的ディスパッチとは

動的ディスパッチ(Dynamic Dispatch)は動的結合遅延結合とも呼ばれ、プログラムの実行時に利用するプロパティやインスタンスを決定することを意味します。

動的ディスパッチが行われるサンプルコードがこちらです。

動的ディスパッチの例
// キャラクターはattack(攻撃)メソッドを持つ
protocol Character {
    func attack()
}

// ゴクウ
class Goku: Character {
    func attack() {
        print("かめはめは!")
    }
}

// ゴハン(ゴクウの子クラス)
class Gohan: Goku {
    override func attack() {
        print("ませんこう!")
    }
}

//isGokuはランダムでtrueもしくはfalseになる
let isGoku: Bool = arc4random_uniform(2) == 0

let character = isGoku ? Goku() : Gohan()
character.attack()

このプログラムでは、isGokuの値がtrueかどうかによって、キャラクターが決定し、その攻撃メソッド(attack)が変わります。

また、isGokuの値は実行時にランダムで決定するため、character.attack()Gokuクラスのattack()が実行されるのかGohanクラスのattack()が実行されるのかはコンパイラは判断できません。
実行時にオブジェクトによって振る舞いが決定する、**ポリモーフィズム(Polymorphism: 多態性)**の例にもなっています。

動的ディスパッチはパフォーマンスを犠牲にするため、実行速度が求められるようなプログラムでは適しません。finalでサブクラスがないことを明示し、privateなどでアクセスレベルを限定することで、コンパイル時に参照するメソッドやプロパティを限定し、動的ディスパッチの可能性を減らすことでよりパフォーマンスの良いプログラムを作成できます。

参考

102
55
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
102
55

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?