5章: 木曜日: クラス定義
はじめに
- Rubyのclassキーワードは、オブジェクトの動作規定だけでなく、実際にコードを実行している
- この章では、クラスマクロ(クラスを修正する方法) / アラウンドエイリアス(メソッドをコードでラップする方法) / 特異クラス(シングルトンクラス) を説明する
クラス定義とは
- クラス定義にはあらゆるコードを置くことができる
- メソッドを定義するだけの場所ではない
class MyClass
puts 'Hello'
end
- メソッドやブロックと同じように、クラス定義も最後の命令文の値を戻す
- クラス(やモジュール)定義の中では、クラスがカレントオブジェクトselfになる
- クラスとモジュールも単なるオブジェクトにすぎない。
result = class Myclass
self
end
result #=> MyClass
カレントクラス
- Rubyのプログラムは、常にカレントオブジェクトselfと カレントクラスを持つ
- メソッドを定義すると、カレントクラスのインスタンスメソッドに
- プログラムのトップレベルでは、カレントクラスは、mainのクラスのObjectになる。
- メソッドの中では、カレントオブジェクトのクラスが、カレントクラスに。
カレントオブジェクト
- self
- インスタンスメソッドが呼び出されるレシーバのこと
Module#class_eval
- クラス名がわからない時にクラスをオープンするために用いる
- クラスのコンテキストでブロックを評価するもの
def add_method_to(a_class)
a_class.class_eval do
def m; 'Hello!'; end
end
end
add_method_to Strign
"abc".m #=> "Hello!"
"abc".m.class #=> String
- class_evalは、selfとカレントクラスを変更する
- カレントクラスを変更することで、クラスを再オープンできる
- classが定数を必要とするのに対し、class_evalはクラスを参照する変数ならなんでも使える
- class_evalは、フラットスコープを持つので、class_evalブロックのスコープの外側の変数も参照できる
instance_evalとclass_evalの使い分け
- クラス以外のオブジェクトをオープンしたいなら、instance_eval
- クラス定義をオープンして、defを使ってメソッドを定義したいなら、class_eval
カレントクラスのまとめ
- Rubyのインタプリタは、常にカレントクラスの参照を追跡
- defで定義された全てのメソッドは、カレントクラスのインスタンスメソッドに。
- クラス定義の中では、カレントオブジェクトselfとカレントクラス(定義クラス)は同じ
- クラスへの参照を持っていれば、クラスはclass_evalでオープンできる
クラスインスタンス変数
-
クラスのインスタンス変数と、クラスのオブジェクトのインスタンス変数は別物
-
クラスのスコープで定義した変数のことを「クラスインスタンス変数」
-
メソッドのスコープで定義した変数を「インスタンス変数」
- 参考 この記事がすごく参考になりました。
-
アクセスできるのはクラスだけであり、クラスのインスタンスやサブクラスからはアクセスできない
class MyClass
@my_var = 1
def self.read; @my_var; end
def write; @my_var = 2; end
def read; @my_var; end
end
obj = MyClass.new
# インスタンスメソッドでアクセスできない。
obj.read #=> nil
obj.write
# インスタンスメソッドwriteで定義したものを呼び出す
obj.read #=> 2
# クラスメソッドで呼び出せる!
MyClass.read #=> 1
クラス変数
-
@@プレフィックスをつけたもの
class C @@v = 1 end
-
クラス変数は、サブクラスや通常のインスタンスメソッドからもアクセスできる
-
クラス変数は、クラスではなく クラス階層に属している
- ハマることがあるので、Rubyistはクラス変数を使わず、クラスインスタンス変数を推奨
特異メソッド
-
単一のオブジェクトに特化したメソッド
- 特定のオブジェクトにメソッドを追加できる
- Object#define_singleton_methodで定義できる
str = "just a regular string"
def str.title?
self.upcase == self
end
self.title? #=> false
str.methods.grep(/title?/) #=> [:title?]
str.singleton_methods #=> [:title?]
Rubyにおける型
- 「型」はオブジェクトが反応するメソッドの集合に過ぎない
- Rubyなどの動的言語では、オブジェクトの「型」はそのクラスとは厳密に結びついていない
- ダックタイピング
- 「型」はオブジェクトが反応するメソッドの集合に過ぎないという、流動的な型のこと。
クラスメソッドの真実
- クラスは単なるオブジェクトであり、クラス名は単なる定数
- クラスメソッドは、クラスの特異メソッド
クラスマクロ
-
クラス定義の中で使えるクラスメソッド
- ex) attr_accessor
特異クラス
- オブジェクトの通常クラスとは別に持つ、特別なクラスのこと
- メタクラスやシングルトンクラスとも
-
特異クラスは、オブジェクトの特異メソッドが住んでいる場所
-
Object#singleton_class
か、class << 構文
によってみることができる
-
- 特異クラスは、インスタンスを一つしか持てない
- だから、 シングルトンクラス
- オブジェクトが特異メソッドを持っていると、Rubyは通常のクラスではなく、特異クラスのメソッドから探索を始める。
Rubyのオブジェクトモデルの7つのルール
- オブジェクトは1種類しかない
- それが、通常のオブジェクトかモジュールに
- モジュールは1種類しかない
- それが通常のモジュール、クラス、特異クラスのいずれかに
- メソッドは1種類しかない
- メソッドはモジュール(大半はクラス)に住んでいる
- 全てのオブジェクト(クラス含む)は、「本物のクラス」を持つ
- それが通常のクラスか特異クラス
- 全てのクラス(BasicObjectを除く)は、一つの祖先を持つ
- あらゆるクラスがBasicObjectに向かって、1本の継承チェーンをもつ。
- オブジェクトの特異クラスのスーパークラスは、オブジェクトのクラス
- クラスの特異クラスのスーパークラスは、クラスのスーパークラスの特異クラス
- メソッドを呼び出すときは、Rubyはレシーバの本物のクラスに向かって、「右へ」煤sみ、継承チェーンを「上へ」進む。
クラスメソッドの構文
- self.メソッドを使う場合
class MyClass
def self.another_class_method; end
end
- << self を使う場合
class MyClass
class << self
def yet_another_class_method; end
end
end
メソッドラッパー
アラウンドエイリアス
- Module#alias_methodを使って、Rubyのメソッドにエイリアスをつける
- alias_methodを使うときは、メソッドの新しい名前を先に、元の名前を後に
class myClass
def my_method; 'my_method()'; end
alias_method :m, :my_method
end
obj = MyClass.new
obj.my_method #=> "my_method()"
obj.m #=> "my_method()"
メソッドの再定義
- 新しいメソッドを定義して、元の名前をつけること
- 元のメソッドを変更することではない
アラウンドエイリアスをまとめると
- メソッドにエイリアスをつける
- メソッドを再定義する
- 新しいメソッドから古いメソッドを呼び出す。
感想
む、、、むずい。なんとか食いついたという感覚
時間を置いてもう一度読み直す。