せかいや

いまいるここを、おもしろく http://sekai-in-the-box.appspot.com/

【Ruby】変数って何?定数って何? ~クラス名が大文字始まりの理由

 
シンボルって、定数っていうかリテラル?みたいな?
という師匠からのふわっとした携帯メールに対処すべく、公式ガイド「変数と定数」を読む。


定数に行き着くまでに「変数」の項目が待ち受けている。。。。



 

クラス自身のインスタンス変数?

クラス変数はクラス自身のインスタンス変数とは以下の点で異なります。
サブクラスから参照/代入が可能
インスタンスメソッドから参照/代入が可能

これって「クラスインスタンス変数」のことだよね。
Rubyはクラスもオブジェクト。だからインスタンス変数を持てる。

今一度理解してみよう。

クラス変数と定数の違い

クラス変数と定数の違いは以下の通りです。
再代入可能(定数は警告を出す)
クラスの外から直接参照できない(継承されたクラスからは参照/代入可能

なるほど。

class Hoge
  @@hoge2
end
Hoge.@@hoge2 = "hhh"

■実行結果

syntax error, unexpected tCVAR, expecting '('Hoge.@@hoge2= "hhh"

なるほど。
selfのコンテキストがそのクラス以外のところで使うとエラーか。


 

クラス変数とクラスインスタンス変数の違い

クラス変数はクラス自身のインスタンス変数とは以下の点で異なります。
サブクラスから参照/代入が可能
インスタンスメソッドから参照/代入が可能

クラス変数は「インスタンスメソッドから参照/代入が可能」で、クラスインスタンスはそうでないと言っている。
確かめてみよう。

クラス変数は「インスタンスメソッドから参照/代入が可能」

class Hoge
  @@hoge2

  def method1  ←(1)
    @@hoge2 = "koko"
  end
  def self.method2  ←(2)
    @@hoge2 = "ppp"
  end
  def self.show    ←(3)
    p @@hoge2
  end
end

Hoge.method2
Hoge.show
Hoge.new.method1
Hoge.show

■実行結果

"ppp"
"koko"

確かにインスタンスメソッドから代入が出来ている(1)


 

クラスインスタンス変数は「そうではない」

class Hoge
  @hoge2
  
  def method1
    @hoge2 = "koko"     ←(4)
    p @hoge2 + " dayo"
  end
  def self.method2
    @hoge2 = "ppp"
  end
  def self.show
    p @hoge2
  end
end

Hoge.method2
Hoge.show
Hoge.new.method1 ←(5)
Hoge.show

■実行結果

"ppp"
"koko dayo"
"ppp"

うん。
(4)の@hoge2は、(5)で作成したHogeインスタンスインスタンス変数とみなされているから、クラスのインスタンス変数が変更されることはない。
このあたりは、Javaだと「シャドウ化」でやったことと似てる。

class Super{
    int height = 178 ;

    void getHeight(int height){//シャドウ化
        height = height ;
    }
}
class Test{
    public static void main(String[] args){
        Super s = new Super() ;
        s.getHeight(180) ;
        System.out.println(s.height) ;//①
    }
}

この例は、誤り。コンパイル、実行はできるが「height = height」がどのheightを指しているかわからない。この例を実行すると、①は178を表示する。すなわち、ローカル変数に180を代入しただけ!という事になる。
代入先をインスタンス変数にするには、左辺のheightがインスタンス変数であることを示すには、キーワードthisを使用すればよい。
すなわち、this.height = height ;

http://www.booran.com/menu/java/method_variable.html


インスタンス変数の宣言方法

初めてインスタンス変数を見たときは

class Hoge
  def method1
    @hoge2 = "koko"
  end
end

っていうのが「なんで @hoge2 を宣言してないの!?」って理解できなくて、
「宣言しても問題ないだろ」と思って↓

class Hoge
  @hoge2
  def method1
    @hoge2 = "koko"
  end
end

と書いて、挙動が不明すぎでパルプンテになったけど
今なら分かる。
インスタンス変数を宣言したつもりが、クラスインスタンス変数を宣言していたんだ。
だから上手く動かなかったんだ。

正しくはこう↓

class Hoge
  attr_accessor :hoge2
  def set_method
    @hoge2 = "koko"
  end
end

hoge = Hoge.new
hoge.set_method
p hoge.hoge2

■実行結果

"koko"

クラス名が大文字始まりの理由

クラス定義式はクラスオブジェクトの生成を行うと同時に、 名前がクラス名である定数にクラスオブジェクトを代入する動作をします。 クラス名を参照することは文法上は定数を参照していることになります。

何だってーーー!

class Foo
end
Foo = "hoge"
p Foo
p Foo.new

■実行結果

warning: already initialized constant Foo
warning: previous definition of Foo was here
"hoge"
in `<main>': undefined method `new' for 
"hoge":String (NoMethodError) ←!!

なんと!クラスがなくなっちゃった!

 

あるクラスまたはモジュールで定義された定数を外部から参照する ためには`::'演算子を用います。

クラス名・モジュール名が定数だと考えると、
このコードも納得だ↓

module Hoge
  class Foo
    def initialize
      p "koko"
    end
  end
end
p Hoge::Foo.new ←Hogeで定義された「Foo」定数を参照してる

■実行結果

"koko"
#<Hoge::Foo:0x3067698>


 

まとめ

師匠のふわっとメール

シンボルは定数っていうか、リテラルって感じ

リテラルって感じ」は置いておくとして。

確かに、大抵のサイトは
「シンボルは数値(Fixnum)みたいなもの」って理解だったけど、

シンボルは、再代入が出来ないことを考えると、
「定数って感じ」もある。
定数は
Foo = "gohan"
みたいに箱のラベル(Foo)と中身(gohan)に関連性はないけど、
数値は
箱のラベルと中身が一致している。
「1」ってラベルには「1」って値が入っている。

それを考えるとシンボルは、数値のほうが近いかもしれない。
まあ、感覚の話ですね。

p :name
p :name.to_s

■実行結果

:name
"name"

こういうのは、経験を積み上げるとまた認識が変わるのでしょう。。