独自クラスの記述と利用方法
クラスを作成する
独自にクラスを作成するにあたって、まずは「犬ロボットのクラス」の例をイメージしてみましょう。
犬ロボットには、「名前」「種類」「誕生日」という属性(プロパティ)を共通して設定し、「歩く」「吠える」という振る舞い(メソッド)を持たせたいと思います。
左の図が犬クラス(設計書)です。
ここから実際に作られる犬ロボットは、犬クラスの設定を共通して持つ事になります。
(どの犬ロボットにも名前と種類と誕生日という属性(プロパティ)を持ち、メソッドに定義された振る舞いを持つ。)
右の図は、犬クラス(設計書)をインスタンス化(実体化)し、実際に犬ロボットを作った例です。
名前や種類など、作成したい犬の属性を設定(=プロパティに値を設定)しています。
クラスはあくまで設計書・定義であり、値を実際に保持したり振る舞いを実際に行うのはインスタンスの役目です。
メソッドの処理(歩く、吠える)は、クラスで定義されたメソッドの内容に従って、インスタンス(実体の犬ロボット)で処理されることになります。
それではどういうことなのか実際に見ていきましょう
クラスの宣言 class クラス名 { ~処理~ }
それでは、独自クラスを作成する記述を見ていきましょう。犬(Dog) クラスを定義する記述は次のようになります。
1 2 3 |
class Dog { // プロパティ・メソッドを記述 } |
クラス名は一般的に最初の一文字目を大文字にします。
プロパティの作成
定義したクラスに、プロパティ(=クラスの中の定数/変数)を作成してみます。
ここでは、初期値に""(空文字)を持つname という名前のプロパティを定義した例を確認してみます。
1 2 3 |
class Dog { var name = "" // プロパティ } |
メソッドの作成
次に、bark (吠える)というメソッド(=クラスの中の関数)を犬クラス内に定義した例を確認してみましょう。
1 2 3 4 5 6 |
class Dog { var name = "" // プロパティ func bark() { // 吠えるメソッド print("ワンワン!") } } |
これで、犬クラス、犬ロボットの設計書ができました。これを使い回して実体を作っていきます。
クラスのインスタンス化
作成した犬クラスを、実際に形をもたせて扱ってみましょう。クラスを書いた下に、次のコードを書いてみてください。
1 2 3 |
var myPet: Dog = Dog() //インスタンス化 myPet.name = "ポチ" // 名前を付ける myPet.bark() // 吠える |
順番に見ていきましょう。
var インスタンス名: クラス名 = クラス名()
var myPet: Dog = Dog()
...myPetというインスタンス名(実体の名前)に型(クラス名)を指定、関数呼び出しに似た書き方でクラスを代入しています。
ここで、この書き方は今までにやってきたことと非常に似通った書き方をしていることに気が付かれると思います。
実は、StringやIntといった基本的な型は、Swiftではクラスとして提供されているのです。
(これはSwiftの基本的な型にてお話ししています。)
今までの型と同じように、型推論で省略することができますし、違う型では代入できない、という特徴を持ちます。
これで、myPetという実体の箱に、Dogクラスの設計書が入り込みました。
インスタンス名.プロパティ
myPet.name = "ポチ"
...myPetは、クラスで定義した設定を元に作成された実体です。クラスで作成したnameプロパティが存在しますので、この書き方でプロパティにアクセスし、そこに具体的なデータを入れてあげます。
これで、myPetという実体の箱に、オリジナルの名前をつけることができました。
インスタンス名.メソッド
myPet.bark()
...関数の呼び出しと同じように、「メソッド名()」と呼び出すのですが、今回はインスタンスの中のメソッドなので「インスタンス名.メソッド名()」という形で呼び出します。
これで、myPetは設計書に書かれた動作に基づいて振る舞います。
イニシャライザ init(引数名: 型) { ~処理~ }
インスタンスの生成時に必ず実行される特殊なメソッド(クラスの中の関数)の事です。
これを使い、インスタンス(呼び出し元)から値を受け取りプロパティ(クラスの中身の値)を設定することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// クラス class Dog { var name = "" // プロパティ init() { // イニシャライザ1 引数が渡されなかった時の設定 self.name = "名無し" } init(name: String) { // イニシャライザ2 引数が1つ渡された時の設定 self.name = name } func bark() { // 吠えるメソッド print("ワンワン!") } } // インスタンス var myPet: Dog = Dog() // 引数をクラスに渡さない → イニシャライザ1が実行 var yourPet: Dog = Dog(name: "ポチ") // 引数を1つクラスに渡す → イニシャライザ2が実行 |
今回、Dogクラスを元に2つのインスタンスを作成しています。(Dog設計書から2体の犬ロボットを作る)
このとき、書き方は以下のようになっています。
呼び出し元のインスタンス: let/var インスタンス名: クラス名 = クラス名(引数名: 値)
呼び出されるクラスの中のイニシャライザ: init(引数名: 型) { -処理- }
引数が複数ある場合は(データ1, データ2)とインスタンス・イニシャライザともに順番を揃えて記載します。
イニシャライザはメソッド、つまり関数ですので、関数で学んだ書き方と同じように引数を記載してください。
上記のプログラムコードを実行してみましょう。
インスタンスmyPet は引数を渡してこなかったので、受け取る引数を設定していない init() が実行されました。
インスタンスyourPet は引数を一つ渡してきたので、受け取る引数が一つに設定されているinit(name: String) が実行されました。
イニシャライザは「受け取る引数ごと」によって複数記載でき※、渡された引数が一致するイニシャライザが実行されます。
(※例:「init(x: String)」 は全く同じ形で複数記載することはできないが、init(x: Int)とすれば、文字列が1つ渡された時と、数値が1つ渡された時とで場合分けされ、別々のパターンとして記載することができる。)
イニシャライザで設定された引数の種類・数ではない形で、インスタンスから引数が渡されると、インスタンス宣言時にエラーが起こります。試しに2つ引数があるインスタンスを上のコードに追加してみてください。
また、イニシャライザの中では定数/変数にself. とつけることで「クラスの中の」定数/変数であると判断されます。
self.をつけないと優先的に引数であると判断されます。
(サンプルコード例のself.name = nameは クラスのプロパティname に 引数として受け取ったnameを代入しています。もちろん、設定したいプロパティと引数を別の名前にしてもOKですが、慣れてきたらこのように表記することでプロパティ名をいくつも考えずに済みますし、何を設定するための引数なのかがわかりやすくなります。)
以上、新しい用語が増え混乱するかと思いますが、1つ1つを分解して見ると、全て今まで行ってきた書き方と同じような動きをしています(プロパティは定数・変数ですし、initは関数です)。過去のテキストを見返しつつ、手を動かしながら動作を確認してみてください。