これは
- Ruby Gold を受験するにあたって学んでいること等を写経したり, メモったりしています
- 「Ruby 技術者認定試験合格教本」を主に参考にしており, 今回は第五章「組み込みクラス」を写経していきます
- 実行例では主に pry を利用していますが, 例外が発生した際の実行過程出力(from から始まる行)は省略しています...すいません
[12] pry(main)> Baz2.new.private_method1 NoMethodError: private method `private_method1' called for #<Baz2:0x005606f46e2778> from (pry):29:in `__pry__'
BasicObject クラス
- Object クラスのスーパークラス
Object クラス
オブジェクト ID
[1] pry(main)> a = "foo" => "foo" [2] pry(main)> a.object_id => 47423007708960 [3] pry(main)> a.__id__ => 47423007708960
- オブジェクト ID は, 同じオブジェクトであれば同じ整数となる
- 同じリテラルであっても, 基本的にはオブジェクト ID は異なる
- TrueClass, FalseClass, NilClass, Symbol, Fixnum クラスのインスタンスは, 同じリテラルであれば同じオブジェクト ID となる
オブジェクトのクラス
[4] pry(main)> "foo".class => String [5] pry(main)> :foo.class => Symbol
- オブジェクトのクラスは
class
メソッドを利用する.
オブジェクトの比較
[1] pry(main)> a = "foo" => "foo" [2] pry(main)> a.hash => -1583099881958492558 [3] pry(main)> a.object_id => 47334052318760 [4] pry(main)> b = "foo" => "foo" [5] pry(main)> b.hash => -1583099881958492558 [6] pry(main)> b.object_id => 47334057818220 [7] pry(main)> a.eql?(b) => true [8] pry(main)> a.equal?(b) => false
eqaul?
メソッドではオブジェクト ID(object_id) を比較するeql?
メソッドはハッシュ(Hash) キーが同じかどうかを比較する==
はオブジェクトの内容が同じかどうかを比較する===
は case 式で利用されるメソッドで, クラスによって結果は依存する
オブジェクトのメソッド一覧
オブジェクトに定義されているメソッドを取得するには, 以下のメソッドを利用する.
メソッド名 | 用途 |
---|---|
methods | 全てのメソッドが呼び出し可能 |
private_methods | プライベートメソッドのみ |
protected_methods | プロテクテッドメソッドのみ |
public_methods | パブリックメソッドのみ |
singleton_methods | 特異メソッドのみ |
以下, 実行例.
[1] pry(main)> class OrenoClass [1] pry(main)* def ore1; end [1] pry(main)* private [1] pry(main)* def ore2; end [1] pry(main)* protected [1] pry(main)* def ore3;end [1] pry(main)* end => :ore3 [2] pry(main)> [3] pry(main)> ore = OrenoClass.new => #<OrenoClass:0x005648754d1c78> [4] pry(main)> ore.methods.grep(/^ore/) => [:ore1, :ore3] [5] pry(main)> ore.private_methods.grep(/^ore/) => [:ore2] [6] pry(main)> ore.protected_methods.grep(/^ore/) => [:ore3] [7] pry(main)> ore.public_methods.grep(/^ore/) => [:ore1] # 特異メソッドの定義 [8] pry(main)> def ore.ore4; end => :ore4 [9] pry(main)> ore.singleton_methods.grep(/^ore/) => [:ore4]
オブジェクトの凍結
オブジェクトの凍結には以下のメソッドを利用する.
- freeze
[1] pry(main)> s = "hello" => "hello" [2] pry(main)> s.freeze => "hello" [3] pry(main)> s.upcase! rescue p $! #<RuntimeError: can't modify frozen String> => #<RuntimeError: can't modify frozen String>
- 標準クラスのオブジェクトでは, 凍結したあとで破壊的なメソッドを呼び出すと例外が発生する
- 凍結状態を確認する場合, frozen? メソッドを使う
- 凍結状態を元に戻すメソッドは用意されていない
凍結状態のオブジェクトでは, 以下のことが出来なくなる.
- インスタンス変数の変更
- 特異メソッドの定義, 特異クラスを使ったメソッド定義
[6] pry(main)> class Cat [6] pry(main)* attr_accessor :name [6] pry(main)* def initialize(name) [6] pry(main)* @name = name [6] pry(main)* end [6] pry(main)* end => :initialize [7] pry(main)> c = Cat.new("Tama") => #<Cat:0x0055b96c489150 @name="Tama"> # インスタンスオブジェクトを freeze [8] pry(main)> c.freeze => #<Cat:0x0055b96c489150 @name="Tama"> # 改めてインスタンスを生成 [9] pry(main)> c = Cat.new("Tama") => #<Cat:0x0055b96c53dba0 @name="Tama"> # 改めてインスタンスを生成したので, 問題なくインスタンス変数の変更が出来ている [10] pry(main)> (c.name = "Piko") rescue p $! => "Piko" # インスタンスオブジェクトを freeze [11] pry(main)> c.freeze => #<Cat:0x0055b96c53dba0 @name="Piko"> # freeze した後なのでインスタンス変数の変更が出来なくなっている [12] pry(main)> (c.name = "Piko") rescue p $! #<RuntimeError: can't modify frozen Cat> => #<RuntimeError: can't modify frozen Cat>
但し, インスタンス変数の参照先のオブジェクトは変更可能.
[1] pry(main)> class Cat [1] pry(main)* attr_accessor :name [1] pry(main)* def initialize(name) [1] pry(main)* @name = name [1] pry(main)* end [1] pry(main)* end => :initialize [2] pry(main)> c = Cat.new("Tama") => #<Cat:0x005578e6c852a8 @name="Tama"> [3] pry(main)> c.freeze => #<Cat:0x005578e6c852a8 @name="Tama"> [4] pry(main)> puts c.name.replace("Piko") Piko => nil
オブジェクトの複製
オブジェクトの複製には以下のメソッドを利用する.
- clone
- dup
以下, 実行例.
[13] pry(main)> a = "hoge" => "hoge" [14] pry(main)> a.object_id => 47434604593020 [15] pry(main)> b = a.clone => "hoge" [16] pry(main)> b.object_id => 47434604585220 [17] pry(main)> c = a.dup => "hoge" [18] pry(main)> c.object_id => 47434603558980
dup と clone は以下のような特徴がある.
メソッド名 | 詳細 |
---|---|
dup | 汚染状態, インスタンス変数, ファイナライザを複製 |
clone | dup が複製する内容に加えて, 凍結状態, 特異メソッドも複製する |
ここでの複製はシャローコピーであり, 自分自身の複製しか出来ない. 配列の要素の参照先は, clone メソッドや dup メソッドでは複製出来ない. また, コピーできないオブジェクト(true, false, nil, シンボル, 数値)に対して clone や dup を呼び出すと、例外 TypeError が発生する.
[1] pry(main)> class Cat [1] pry(main)* attr_accessor :name [1] pry(main)* def initialize(name) [1] pry(main)* @name = name [1] pry(main)* end [1] pry(main)* end => :initialize [3] pry(main)> original = Cat.new("Tama") => #<Cat:0x0055996c057610 @name="Tama"> [4] pry(main)> copied = original.clone => #<Cat:0x0055996c08df80 @name="Tama"> [5] pry(main)> puts copied.equal?(original) false => nil [6] pry(main)> puts copied.name Tama => nil
copied の @name に対して破壊的なメソッド(レシーバ自身を変更するメソッド)を呼び出すと, original の @name が指している文字列も変更される.
[7] pry(main)> copied.name.replace("Mike") => "Mike" [8] pry(main)> puts original.name Mike => nil
未定義メソッドの呼び出し
- オブジェクトに定義されていないメソッドが呼び出された際, Ruby は method_missing メソッドを呼び出す
- method_missing メソッドが定義されていない場合は NoMethodError になる
- レシーバが指定されていない場合は NameError になる場合がある
[1] pry(main)> class Bar [1] pry(main)* def method_missing(name, *args) [1] pry(main)* puts name [1] pry(main)* end [1] pry(main)* end => :method_missing [2] pry(main)> b = Bar.new => #<Bar:0x0056283201a878> # method_missing が呼ばれる [3] pry(main)> b.hoge hoge => nil # レシーバが指定されていないので NameError となる [4] pry(main)> hoge NameError: undefined local variable or method `hoge' for main:Object
オブジェクトの文字列表現
- オブジェクトの文字列表現を求めるには, to_s メソッドや inspect メソッドを使う
- ほとんどの場合, サブクラスで定義し直されている
- to_s メソッドは内容や値の文字列表現を返す
- inspect メソッドはオブジェクトを人間が読める形式に変換する
[5] pry(main)> a = 1.2 => 1.2 [6] pry(main)> a.to_s => "1.2" [7] pry(main)> a.inspect => "1.2" [8] pry(main)> Object.new.inspect => "#<Object:0x005628322068f8>"
以上
写経でした.