Ruby

Rubyのインスタンス変数・メソッドとクラス変数・メソッドを検証

More than 1 year has passed since last update.

Rubyのインスタンス変数・メソッドとクラス変数・メソッドを検証する

Rubyを勉強していて、インスタンス変数・メソッドとクラス変数・メソッドの扱いについて理解するのに時間を要したので、そのときの検証内容を備忘録として記載。
ここではRubyのバージョンは2.4.1を利用。

クラスで検証

まずは基本仕様。

  • インスタンス変数はインスタンスメソッドしかアクセスできない
  • クラス変数はインスタンスメソッドとクラスメソッドの両方アクセスできる
  • クラスインスタンス変数はそのクラス内で定義されたクラスメソッドしかアクセスできない

Rubyの検証用サンプルコード

class Test
  @c_i_var = 0
  @@c_var = 10
  var = 'var'

  def initialize
    @i_var = 100
  end

  def test
    @i_var   += 1
    @@c_var   += 1
    self
  end

  def examine1
    p 'Test::examine1'
    p @i_var, @c_i_var, @@c_var
    self
  end

  def Test.examine2
    @c_i_var += 1
    p 'Test::examine2'
    p @i_var, @c_i_var, @@c_var
    Test.new
  end

  def examine3
    Test
  end

  p 'Test::var'
  p var
end

class Test2 < Test
  def Test2.examine2
    p 'Test2::examine2'
    p @i_var, @c_i_var, @@c_var
  end
end

t = Test.new
t.test.examine1
Test.examine2.test.examine1.examine3.examine2

Test2.examine2

サンプルコードの出力結果

"Test::var"
"var"
"Test::examine1"
101
nil
11
"Test::examine2"
nil
1
11
"Test::examine1"
101
nil
12
"Test::examine2"
nil
2
12
"Test2::examine2"
nil
nil
12

判明したこと

  • クラスTest1のインスタンスメソッドexamin1とクラスメソッドexamin2の実行結果から「インスタンス変数はインスタンスメソッドしかアクセスできない」と「クラス変数はインスタンスメソッドとクラスメソッドの両方アクセスできる」が判明。
  • クラスTest1とクラスTest2のクラスメソッドexamine2の実行結果から、「クラスインスタンス変数はそのクラス内で定義されたクラスメソッドしかアクセスできない」が判明
  • クラス内でローカル変数を定義してもエラーにならない。またクラス内で定義されたローカル変数はインスタンスメソッドとクラスメソッドの両方からアクセスできない。p varが実行されるのは最初のインスタンス生成時の1度だけ。
  • クラス変数とクラスインスタンス変数の値はインスタンス間で共有される
  • インスタンス変数はインスタンス間で共有されない

その他

クラス変数を定義すると"bad code style"という警告がでるので、実際の運用では使わないようにする。

浅いコピーと深いコピーをしたときのクラス変数とインスタンス変数

こちらでPython3で浅いコピーと深いコピーをしたときのクラス変数とインスタンス変数の変化について検証したが、Rubyでの挙動も確認した。
結果は、Python3と同様に深いコピーでもクラス変数の値が変更されている。

Rubyの検証用サンプルコード

class Test
  @@c_var = 2

  def initialize(val)
    @i_var = val
  end

  def inspect
    '%d, %d' % [@@c_var, @i_var]
  end

  def examine1
    @i_var += 2
    self
  end

  def examine2
    @@c_var += 2
    self
  end
end

t1 = Test.new(3)
t1.examine1.examine2
p t1
p Test.new(3)

puts '--- shallow copy ---'

t2 = t1.clone
t2.examine1.examine2
p t1
p t2
p Test.new(3)

puts '--- deep copy ---'

t3 = Marshal.load(Marshal.dump(t1))
t3.examine1.examine2
p t1
p t2
p t3
p Test.new(3)

サンプルコードの出力結果

4, 5
4, 3
--- shallow copy ---
6, 5
6, 7
6, 3
--- deep copy ---
8, 5
8, 7
8, 7
8, 3