【Ruby】クラスとモジュール
- 作者: 高橋征義,後藤裕蔵,まつもとゆきひろ
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2013/06/04
- メディア: 単行本
- この商品を含むブログ (22件) を見る
今回は8章「クラスとモジュール」
クラス
ary = [] str = "Hello" p ary.class #=> Array p str.class #=> String # オブジェクトがどのクラスに属しているか
p ary.instance_of?(Array) #=> true p str.instance_of?(String) #=> true p ary.instance_of?(String) #=> false p ary.instance_of?(Array) #=> false # 特定のクラスのインスタンスかどうかの判断
p ary.is_a?(Array) #=> true p ary.is_a?(Object) #=> true # 継承関係をさかのぼって判断できる
クラスを作る
class HelloWorld #=> class 文 def initialize(myname = "Ruby") #=> initialize メソッド @name = myname #=> インスタンス変数の初期化 end def hello #=> インスタンスメソッド puts "Hello, world. I am #{@name}." end end secon = HelloWorld.new("secon") alice = HelloWorld.new("Alice") ruby = HelloWorld.new secon.hello #=> Hello, world. I am secon. ruby.hello #=> Hello, world. I am Ruby.
この例を細分化する。
class 文
class HelloWorld # クラス名は必ず大文字で始める クラスの定義 end
initialize メソッド
new メソッドによってオブジェクトを生成すると、この文が呼ばれる。そのとき、new に渡した引数がそのまま渡される。
def initialize(myname="Ruby") @name = myname end # 引数 myname を受け取っている
secon = HelloWorld.new("secon") # initialize メソッドに "secon" を渡している
ruby = HelloWorld.new # 引数を渡さなかった場合はデフォルト値(今回の場合は "Ruby")が渡される
インスタンス変数とインスタンスメソッド
def initialize(myname="Ruby") # initialize メソッド @name = myname # インスタンス変数の初期化 end
@ ではじまる変数はインスタンス変数であり、同じインスタンス内であればメソッド定義を越えてその値を参照、変更できる。
また、インスタンスごとに違う値を持つこともでき、インスタンスが存在している間は値を保持しておいて何度でも利用できる。
secon = HelloWorld.new("secon") alice = HelloWorld.new("alice") ruby = HelloWorld.new # それぞれ異なる @name を保持
インスタンス変数はインスタンスメソッドから参照できる。
hello メソッドでは @name を利用している。
class HelloWolrd ... def hello # インスタンスメソッド puts "Hello, world. I am #{@name}." end end
HelloWorld クラスのインスンタンスに対して hello メソッドを呼び出すと、initialize メソッドで設定された @name の値が使われる。
secon.hello #=> Hello, world. I am secon. ruby.hello #=> Hello, world. I am Ruby.
アクセスメソッド
Ruby では、オブジェクトの外部からインスタンス変数を直接参照したり、インスタンス変数に代入したりすることができない。オブジェクトの内部の情報にアクセスするためには、そのためのメソッドを定義する必要がある。
HelloWorld クラスの @name にアクセスするために、に次のメソッドを追加する。
class HelloWorld ... def name # @name を参照する @name end def name=(value) # @name を変更する @name = value end end
p alice.name #=> "alice" # @name の値を返す
alice.name=("Ellie") # @name の値を変更 p alice.name #=> "Ellie"
これらのメソッドを簡単に定義するために、次のようなメソッドが用意されている。
定義 | 意味 |
---|---|
attr_reader :name | 参照のみ可能(name メソッドを定義) |
attr_writer :name | 変更のみ可能(name= メソッドを定義) |
attr_accessor :name | 参照と変更の両方が可能(上記2つを定義) |
class HelloWorld attr_accessor :name # 同様の意味となる end
self メソッド
インスタンスメソッドの中で、メソッドのレシーバ自身を参照するときに使う。
参照したいメソッドが定義されていなければ当然呼び出すことは出来ない。
class HelloWorld ... def greet puts "Hi, I am #{self.name}." #greet メソッドを呼んだ時のレシーバを参照 end end
レシーバを省略してメソッドを呼ぶと、暗黙に self をレシーバとする。
def greet print "Hi, I am #{name}." end
「=」で終わるメソッドを呼び出す場合はレシーバを明示して、「self.name = "Ruby"」という形式で呼ぶ必要がある。
def test name = "Ruby" self.name = "Ruby" end
・・・この文が何をやっているか、いまいちよく分からない。
クラスメソッド
クラスメソッドはインスタンスに対する操作ではなく、そのクラスに関連する操作のために使われる。
class << HelloWorld def hello(name) puts "#{name} said hello." end end HelloWorld.hello("John") #=> John said hello.
「class << クラス名 ~ end」という書き方のクラス定義を特異クラス定義という。
また、特異クラスで定義したメソッドを特異メソッドという。
次のような形式でクラス定義することも出来る。
def HelloWorld.hi(name) puts "#{name} said Hi." end HelloWorld.hi(Taro) #=> Taro said Hi.
クラス定義の中でクラスメソッドを追加する場合は、「class << self ~ end」として、その中にメソッドを記述することも出来る。
class HellWorld class << self def hello(name) puts "#{name} said hello." end end end
クラス定義の中なら次のような形式で定義することもできる。
class HelloWorld def self.hi(name) puts "#{name} said Hi." end end
クラス変数
「@@」で始まる変数で、そのクラスの全てのインスタンスで共有できる変数のこと。定数と違い、何度でも値を変更することが出来る。
class HelloCount @@count = 0 # hello メソッドの呼び出し回数 def HelloCount.count # 呼び出し回数を参照するためのクラスメソッド @@count end def initialize(myname="Ruby") @name = myname end def hello @@count += 1 # 呼び出し回数を加算 puts "Hello, world. I am #{@name}." end end secon = HelloCount.new("secon") alice = HelloCount.new("alice") ruby = HelloCount.new p HelloCount.count #=> 0 secon.hello alice.hello ruby.hello p HelloCount.count #=> 3
メソッドの呼び出しを制限
Ruby では3種類のアクセス制限のレベルが用意されている。
- public
メソッドを、インスタンスメソッドとして使えるように公開する
- private
メソッドを、レシーバを指定して呼び出せないようにする(インスタンスの外側から利用できなくする)
- protected
メソッドを、同一のクラスであればインスタンスメソッドとして使えるようにする
メソッドのアクセス制限を変更するには、これらのキーワードにメソッド名を表すシンボルを指定する。
class AccTest def pub puts "pub is a public method." end public :pub # pub メソッドを public に設定(指定しなくてもよい) def priv puts "priv is a private method." end private :priv # priv メソッドを private に設定 end acc = AccTest.new acc.pub #=> pub is a public method. acc.priv #=> private method `priv' called for #<AccTest:0x00000002fc3760> (NoMethodError) # priv メソッドを呼び出そうとすると例外が発生する
複数のメソッドをまとめて同じアクセス制限に定義したい場合は次のようにすることもできる。
private # 引数を指定しなければ、これ以降に定義したメソッドは private になる def priv puts "priv is a private method." end def priv2 puts "priv2 is a private method." end
initialize メソッドは特別で、常に private として定義される。
protected は、同一クラス(とそのサブクラス)以外の場所からは呼び出せないようにする。
class Point attr_accessor :x, :y # アクセスメソッドを定義 protected :x=, :y= # x= と y= を protected def initialize(x=0.0, y=0.0) @x, @y = x, y end def swap(other) # x,y の値を入れ替えるメソッド tmp_x, tmp_y = @x, @y @x, @y = other.x, other.y other.x, other.y = tmp_x, tmp_y #同一クラス内では呼び出し可能 return self end end p0 = Point.new p1 = Point.new(1.0, 2.0) p [ p0.x, p0.y ] #=> [0.0, 0.0] p [ p1.x, p1.y ] #=> [1.0, 2.0] p0.swap(p1) p [ p0.x, p0.y ] #=> [1.0, 2.0] p [ p1.x, p1.y ] #=> [0.0, 0.0] p0.x = 10.0 #=> NoMethodError
長くなりそうなので一旦ここまで。