Ruby TIPS
クラスとそのコンストラクター/アクセサー/メソッドを定義し利用するには?
Rubyプログラミングの基本中の基本として、クラスの定義から、そのインスタンスの作成・利用、インスタンスメソッドの定義、変数へのアクセスまでを説明する。
クラスを利用すれば、目的に合わせて変数や手続き(=メソッド)をひとまとめにできる。一般に、クラスを定義し、それを基にクラスの実体(=インスタンス)を作って利用するというのが、クラスの初歩的かつ基本的な利用方法である。今回は、Rubyにおけるクラスの定義、インスタンスメソッドの定義、変数へのアクセス、インスタンスの作成と利用など、基本の基本を押さえることとしよう。
クラスの定義と利用
クラスとは、大ざっぱにいえば変数とメソッドをひとまとめにしたものである。例えば、人間を表現するには、「身長」や「体重」といった変数と、「食べる」「寝る」といったメソッドをひとまとめにしておくと便利である。そのようにすれば、食べると体重が増える、といった処理を、別々に定義された変数とメソッドを使って表すよりも、簡潔に書ける。
とはいえ、あまり抽象的な話をしても実感が湧かないので、簡単な例で見ていこう。経験のある人には自明な話なので、次の節まで読み飛ばしてもらっても構わない。
さて、ここでは、平面上の点を表すクラスを例として取りあげよう。平面上の点は(デカルト座標系なら)、「X座標」と「Y座標」という変数で表される。メソッドとしては、「点の座標を設定するメソッド」や「点の座標を取得するメソッド」が考えられる。他にも、点を移動させるメソッドや原点の周りに回転させるメソッドなども考えられるが、今は、深入りはしないことにしよう。
ここでは、クラスにPoint
という名前を付けることにする。以下の図の左側がPoint
クラスに含まれる変数とメソッドである。インスタンスの作成(=インスタンス化)については後述する。
Point
クラスにはX座標とY座標を表す変数があり、座標の設定と座標の取得を行うメソッドがある。UML(Unified Modeling Language)のクラス図の書き方に沿っているので、項目の前に書かれている「-」はprivate
を表し、「+」はpublic
を表すのだが、取りあえずは気にしないでいてほしい。クラスの定義を基にインスタンスを作成すれば、実際の座標が表せる。
では、図の左側に描かれているクラスの定義をRubyのコードで書いてみよう。クラスを定義するには、class
の後にクラス名を書き、最後をend
で閉じる。まずは大枠から書いてみる(リスト1.1)。
class クラス名
……変数やメソッドの記述……
end
|
クラス定義の大まかな形式。変数やメソッドの記述はこの後で見ていく。
クラス内の変数やメソッドはまとめてメンバーと呼ばれる。
続いて、メンバーも書いてみよう。X座標を表す変数は@x
とし、Y座標を表す変数は@y
とする。詳細は後述するが、変数名の先頭が@
になっていることに注目。
class Point
def set_point(x, y)
@x = x
@y = y
end
def get_point
return @x, @y
end
end
|
set_point
メソッドは変数@x
と変数@y
に値を設定するメソッド、get_point
メソッドは変数@x
と変数@y
の値を返すメソッドである。
メソッド(=インスタンスメソッド)の書き方に特に難しい点はない。気になるのは@
で始まる変数名であろう。このように、@
で名前が始まる変数はインスタンス変数と呼ばれるもので、作成されたインスタンスの中でのみ使われる。インスタンスとは、いわばクラスの定義に基づいて作られた「実体」のようなものである。
では、インスタンスを作成(=インスタンス化)してみよう。クラスからはインスタンスをいくつでも作成できる。プログラムの後半を以下に示す。書き方を確認しよう。
p1 = Point.new
p1.set_point(10, 8)
p p1.get_point
p2 = Point.new
p2.set_point(7, 12)
p p2.get_point
|
new
メソッドは新しいインスタンスを作成するためのメソッド。Point
クラスを基にインスタンスを作成し、変数p1
で参照できるようにした。その後、set_point
メソッドで(10,8)
という座標を設定した。さらにもう一つインスタンスを作成し、変数p2
で参照できるようにした。その後、set_point
メソッドで(7,12)
という座標を設定した。
クラス名.new
と書けば、クラスを基にインスタンスが作成できる。その時点ではまだ座標の値は設定されていないが、実際の「点」が作成されたことになる。その後、set_point
メソッドで座標の値を設定する。変数@x
と変数@y
はインスタンスごとに作成されるので、インスタンスp1
とインスタンスp2
とでは、異なる値が設定されている。念のため、前掲の図1.1を、リスト1.2を反映したようなものに描き変えて掲載しておこう(図1.2)。
Point
クラスの定義を基に2つのインスタンスを作成した。リスト1.2と比較して見てみると、インスタンスのp1
とp2
が新たに描き加えられたことが分かる。
実行例も確認しておこう。get_point
メソッドによって返された変数@x
と変数@y
の値を、p
メソッドで出力しているだけである。
$ ruby sample001.rb
[10, 8]
[7, 12]
$
|
インスタンスp1
の変数@x
の値は10、変数@y
の値は8となり、インスタンスp2
の変数@x
の値は7、変数@y
の値は12となる。
アクセサーの利用
アクセサーを利用すれば、いちいちメソッドを定義しなくてもいいので、クラス定義の記述が簡潔になる。また、インスタンス変数に値を設定したり、値を取得したりするコードも簡単に書ける(リスト1.3)。
class Point
attr_accessor :x, :y
end
p1 = Point.new
p1.x = 4
p1.y = 5
p [p1.x, p1.y]
|
attr_accessor
の後に、変数のシンボルを記述する。この例であれば、:x
がインスタンス変数@x
に対応し、:y
がインスタンス変数@y
に対応する。「インスタンス名.シンボル」という書き方(先頭の:
は除く)でインスタンス変数にアクセスできる。
「:」で始まる名前はシンボルと呼ばれるもので、変数やメソッドを表すのに使われるオブジェクトである。変数の値を利用するためのものではなく、変数そのものを取り扱うために使われるもの、とでも考えるといいだろう。概念の理解が難しいようであれば、attr_accessor
の後に書かれたシンボルが:x
なら、@x
という名前のインスタンス変数が作られ、x
という名前でアクセスできるようになる、と覚えておけばいい。インスタンス変数に値を設定したり、インスタンス変数の値を利用したりする場合には、インスタンス名のp1
に続けて.
を書き、p1.x
のようにすればよい。
なお、attr_accessor
の代わりにattr_reader
と書けば読み出し専用のアクセサーになる。その場合、p1.x = 4
やp1.y = 5
というコードはエラーになる。attr_writer
と書けば、読み書きが可能なアクセサーになるので、attr_accessor
と書いた場合と同じ結果になる(※「writer」という単語は、意味的には「書き込み専用」だが、読み出しもできることに注意してほしい)。
実行結果は以下の通り。
$ ruby sample002.rb
[4, 5]
$
|
アクセサーを利用して、インスタンスp1
の変数@x
に4を、変数@y
に5を代入し、そのまま結果を表示した
変数@x
や変数@y
はインスタンスの中でのみ使われるので、当然のことながらp1.@x
やp1.@y
とは書けない。もちろん、インスタンスの中のメソッドからであればインスタンス変数が使えるので、以下のように書けば、X座標の値とY座標の値をまとめて設定したり、まとめて返したりできる。
class Point
attr_accessor :x, :y
def set_point(x, y)
@x = x
@y = y
end
def get_point
return @x, @y
end
end
p1 = Point.new
p1.x = 4
p1.y = 5
p p1.get_point
|
アクセサーを利用して設定したインスタンス変数をget_point
メソッドの中で利用している。シンボルを表す:x
、インスタンス変数を表す@x
、アクセサーとして利用されるx
のそれぞれの書き方の違いに注意しよう。実行結果は実行例1.2と同じ。
コンストラクターを書く
コンストラクターとは、インスタンスが作成された時に自動的に実行されるメソッドのことである。Rubyではinitialize
という名前のメソッドがコンストラクターになる。コンストラクターは、インスタンスの初期設定などに使われる。
これも簡単な例で見てみよう。これまではインスタンスを作成してから、座標を設定していたが、以下のコードでは、インスタンスの作成時に座標の設定ができる。
class Point
def initialize(x, y)
@x = x
@y = y
end
def set_point(x, y)
@x = x
@y = y
end
def get_point
return @x, @y
end
end
p1 = Point.new(3, 8)
p p1.get_point
|
リスト1.1にinitialize
メソッドを追加し、インスタンス変数@x
と@y
に値を設定している。インスタンスを作成する時にinitialize
メソッドが自動的に呼び出させる。initialize
メソッドの仮引数であるx
とy
に、new
メソッドに指定した実引数の3と8が渡される。
この例では、initialize
メソッドとset_point
メソッドの働きは全く同じになっているが、initialize
メソッドは暗黙的にprivate
なメソッドになるので、例えば、p1.initialize(10, 12)
のような呼び出し方はできない。一方、コンスタラクター以外のメソッドはpublic
なメソッドになる。従って、set_point
メソッドについては、p1.set_point(10, 12)
のような呼び出し方ができる。
とはいえ、このように同じ内容のメソッドが2つあるコードは冗長なので、なんとなく落ち着かない。そこで、alias文を使ってinitialize
メソッドにset_point
という別名を付けて、メソッドの定義を一つにまとめてみよう。alias
文の書き方は、
alias 新しいメソッド名 元のメソッド名
である。ただし、単純に別名を付けるだけだと、set_point
メソッドもinitialize
メソッドと同じようにprivate
なメソッドになってしまうので、public
宣言もしておく。
class Point
def initialize(x, y)
@x = x
@y = y
end
alias set_point initialize # メソッドに別名を付ける
public :set_point # set_pointメソッドをpublicにする
def get_point
return @x, @y
end
end
p1 = Point.new(3, 8)
p1.set_point(12, 9) # 座標を変更する
p p1.get_point
|
alias
文を使ってinitialize
メソッドにset_point
という別名を付ける。さらに、set_point
メソッドをpublic
にしておく。public
の後にはメソッドのシンボル名を指定することに注意。initialize
メソッドはprivate
のままである。
実のところ、わざわざalias
を使って別名を付けたり、public
を指定したりしなくても、リスト1.5のinitialize
メソッドからset_point
メソッドを呼び出すようにするだけでもいい。本来は、その方が簡単で分かりやすいのだが、こういうこともできるという書き方の例として示しておいた。念のため、initialize
メソッドからset_point
メソッドを呼び出した場合のコードと実行例も示しておこう。
class Point
def initialize(x, y)
set_point(x, y)
end
def set_point(x, y)
@x = x
@y = y
end
def get_point
return @x, @y
end
end
p1 = Point.new(3, 8)
p1.set_point(12, 9) # 座標を変更する
p p1.get_point
|
メソッドから同じクラス内の別のメソッドも、もちろん呼び出せる。ここではinitialize
メソッドに渡された引数をそのままset_point
メソッドに渡して呼び出している。
$ ruby sample006.rb
[12, 9]
$
|
new
メソッドで設定した座標をset_point
メソッドで変更しているので、実行結果が[12, 9]となることは容易に分かる。
クラスメソッドを定義する
ここまでは、クラスを基にインスタンスを作成し、インスタンスメソッドを定義し、それを呼び出して利用する例を見てきたが、(インスタンスではなく)クラスそのものの機能として、メソッドを定義することもできる。それがクラスメソッドである。
クラスメソッドはインスタンスを作らなくても利用できる。例えば、原点(0,0)
を返すメソッドは、それぞれのインスタンスによって異なる値を扱うわけではないので、クラスメソッドにした方が都合がいい。リスト1.6にクラスメソッドを追加してみよう。
class Point
def Point.origin # クラスメソッドの定義
return 0, 0
end
def initialize(x, y)
set_point(x, y)
end
def set_point(x, y)
@x = x
@y = y
end
def get_point
return @x, @y
end
end
p Point.origin
|
クラスメソッドを定義するには、メソッドを定義するときに、クラス名.メソッド名
という名前を指定すればよい。ここでは、origin
というメソッド名にしてある。最後の行でクラスメソッドを呼び出しているが、インスタンスを作成していないことに注目。
クラスメソッドはクラス名.メソッド名
という名前で定義する。例えばPoint
クラスのorigin
メソッドであれば、Point.origin
という名前で定義すればよい。クラスメソッドを呼び出すときには、インスタンスを作成するのではなく、単にクラス名.メソッド名
と書けばよい。ここでは、引数のないメソッドを定義しているが、もちろん引数を指定することもできる。実行例は示すまでもないだろう([0, 0]となる)。
なお、Rubyでは個々のオブジェクトに対してメソッドを定義できる。そのようなメソッドのことを特異メソッドと呼ぶ。実は、クラスメソッドも特異メソッドの一つである。蛇足ながら、特異メソッドの例を示しておこう。
str = "Hello"
def str.longer(target)
return (self.length > target.length)? self : target
end
p str.longer("Ruby")
|
変数str
はString
オブジェクトを参照する。ここでは、str.longer
という特異メソッドを定義している。このメソッドの内容は、自分自身と引数で指定した文字列の長い方を返すというもの。ただし、同じ長さの場合は引数に指定した文字列が返される。実行例は省略する("Hello"と表示されるだけなので)。
このようにして、Rubyでは、オブジェクトにメソッドを追加することもできる。クラスもオブジェクトの一種なので、特異メソッドを定義すれば、クラスメソッドとして利用できるというわけである。
今回はクラスの基本を一通り見たが、継承などの機能については触れる余裕がなかった。次回は継承も含め、演算子の定義やオーバーライドなど、高度な機能をいくつか取り上げることとしたい。
まとめ
クラスを定義し、それを基にインスタンスを作成すれば、同じ形式のオブジェクトをいくつも作成できる。インスタンス変数はそれぞれのインスタンスで使われる変数であり、アクセサーを利用すれば簡単に値の設定や取得ができる。インスタンスの作成時にはinitialize
という名前のメソッドが自動的に呼び出される。そのメソッドのことをコンストラクターと呼ぶ。クラスにはインスタンスを作成しなくても利用できるクラスメソッドを定義できる。
処理対象:alias文 カテゴリ:文法 > クラス > クラス/メソッドの定義に関する操作
※以下では、本稿の前後を合わせて5回分(第10回~第14回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
10. for文 ― ちょっと便利な繰り返し処理の構文とは?(2)
Rubyに用意されている繰り返し処理の構文のうち、for文の基本的な使い方と配列と組み合わせた利用例を解説。範囲や配列を利用して一定の回数だけ繰り返して文を実行する方法や、配列全体を処理する方法を紹介する。
11. ブロック ― ちょっと便利な繰り返し処理の構文とは?(3)
Rubyに用意されている繰り返し処理の構文のうち「ブロック」を使えば、繰り返し処理をより簡潔に書ける。その基本的な使い方と、自作メソッドでの利用例を解説する。
12. 【現在、表示中】≫ クラスとそのコンストラクター/アクセサー/メソッドを定義し利用するには?
Rubyプログラミングの基本中の基本として、クラスの定義から、そのインスタンスの作成・利用、インスタンスメソッドの定義、変数へのアクセスまでを説明する。
13. クラスを継承するには? メソッドの呼び出しをprivate/protectedで制限するには?
オブジェクト指向言語の特長である「クラスの継承」をRubyで実現する方法を解説。スーパークラスのメソッドの呼び出し制限で、Ruby言語特有の内容についても紹介する。
14. クラスのメソッドをオーバーライドするには?
継承先クラスの新メソッドで元クラスの既存メソッドをオーバーライドして異なる機能に置き換える方法と、新メソッド内から既存メソッドを呼び出すことで既存機能に新機能を追加する方法を説明する。