2014年6月8日日曜日

Swift言語の入門 (7) クラス編

さてさてお待ちかね、Swiftにおけるクラスの扱いについていろいろと見ていこうと思います。Objective-Cでは、プログラムを書く上で、必要なコンポーネントは大体クラス化されており、一部C言語由来のものなどに対して構造体を使ったり、定数のまとまり等に列挙を使う程度のイメージでしたが、Swiftではそのバランスが大きくことなりそうです。

Swift言語の入門 (2) 列挙型編Swift言語の入門 (5) 構造体編などで説明したとおり、これら二つの要素もクラスに引けを取らない機能を備えており、かつクラスとは全く異なる特徴を兼ね備えています。

この章ではこれらの違いも確認しながら、クラスの定義から利用方法までをひととおりみていきます。

1.宣言


もっとも小さいクラスの宣言は、以下の通りです。

class Player{
}

宣言と実装のように記述を二つ書く必要はなく、ただ単にクラスキーワード、クラス名、中括弧で終わりです。

プロパティや関数を宣言するには、中括弧の中にかきます。

struct Point {
    var x:Double
    var y:Double
}

class Player{
    var position:Point = Point(x:0, y:0);
    func moveTo(p:Point){
        position = p;
    }

}

このPlayerクラスは、positionという名前の座標情報を保持するプロパティを持っており、デフォルトでは(0, 0)です。moveToという関数はプレイヤーを任意の位置に移動させる関数です。構造体のときには、mutating修飾子が必要でしたが、クラスは基本的に内部状態を変えながら動くものなので、クラスで宣言される関数にmutating修飾子をつける必要はありません。そのため、内部状態を頻繁に変えるようなものにはクラスを用いて、そうでないものには構造体を採用するというのが自然な使い方になります。

ここではpositionに初期値を設定してありますが、設定しないとコンパイルエラーになります。その理由は、現状Playerクラスはイニシャライザ(コンストラクタに相当)を持っていないためpositionが初期化されず、またpositionの型はPoint型でnilを受け付けないからです。なので、Point型の最後に「?」や「!」をつけてオプションにするか、イニシャライザの中でpositionを初期化すればコンパイルエラーは起きなくなります。

2.インスタンスの生成


クラスのインスタンスを生成するには、ただ単にクラス名の後に「()」をつけ、引数を記述します。それぞれの引数には変数名をつける必要があります。

var player = Player(position:Point(x:1, y:2))

これらの変数名は、外部変数名と呼ばれ、通常イニシャライザの内部処理を記述するときにそのまま内部変数名として使われますが、以下のように書くことで、外部変数名と内部変数名を別の文字列にすることが可能です。

init(position p:Point){
    self.position = p

}

positionが外部変数名で、pは内部変数名になります。引数に外部変数名をつけられるという仕様はSmalltalkからObjective-C、そしてSwiftへと受け継がれてきており、コードを読みやすくすると同時に、外部変数名の違いによる関数のオーバーロードを行う等、他の言語にはない独特の特徴を実現しています。

3.関数の呼び出し


クラスのインスタンスの持つ関数を呼び出すには、構造体や列挙型とおなじく、「.」のあとに関数名を書きます。

player.moveTo(Point(x:100, y:-100))

引数の外部変数名は、関数側で明示的に書かない限り、呼び出し時には指定せず、内部変数名を外部変数名として指定した場合には、コンパイルエラーになります。

4. クラスの継承


クラスを継承して新たなクラスを作成するには、以下のようにクラス名の後に「:」を書き、その後に親クラスの名前を書きます。継承はクラスのみに許されている機能で、他のクラスの機能を受け継ぎながら、新たな機能を追加し再利用するための機構です。

class RoboPlayer : Player{
    init(){
        super.init(position:Point(x:6, y:0))
    }
    
    override func moveTo(p:Point){
        //ロボはたまに言うことをきかない、とか
    }
}

子クラスは、自動的に親クラスの持つイニシャライザを継承します。また、親クラスの持つ関数をオーバーライド(上書き)したいときには、override修飾子をつける必要があります。これにより、うっかり全く同じ名前の関数を作ってしまうという問題を防いでいます。

子クラスのイニシャライザでは、親クラスのイニシャライザを呼び出す必要があります。

5.デイニシャライザ(Deinitializer)


クラスのインスタンスが解放されるときには、デイニシャライザと呼ばれる特別な関数が実行されます。デイニシャライザは引数をとらず、書き方は以下の通りです。

class DeinitialzerTest{
    deinit{
        
    }
}

総括