【Ruby】クラスの基本について

スポンサーリンク

Rubyのクラスの基本についてです。以前書いたメソッドと、変数・定数について関係してるところもあるのでリンク貼っておきます。

クラスとは

Rubyは全てのデータがオブジェクトです。また、オブジェクトは例外なくなんらかのクラスに属しています。オブジェクトがどのクラスに属しているのか確認するには.classメソッドを使用します。

"hoge".class
#=> String
100.class
#=> Fixnum
[1, 2, 3].class
#=> Array
false.class
#=> FalseClass

オブジェクトがあるクラスのインスタンスかどうかを判断するには.instance_of?(klass)メソッドを使います。

ary = Array.new()
p ary.instance_of?(Array)
#=>true
p ary.instance_of?(String)
#=>false

オブジェクトに割当られている重複しないID(整数)を確認するには.object_idメソッドを使います。

"hoge".object_id
=> 70283213979680
"fuga".object_id
=> 70283211279280

クラスを作る

クラスを作るための基本的な構文は次の通りです。クラス名は必ず大文字で初めなければいけません。

class クラス名
    クラスの定義
end

initializeメソッド

クラスのインスタンスをを生成するには.new()メソッドを使います。initialize メソッドがある場合は、new メソッドを使うと必ず呼ばれます。イメージ的にはJavaのコンストラクタです。次の例ではデフォルト引数が指定されていますが、引数無しでも問題ありません。

class Foo
    def initialize(str="hoge")
        p str
    end
end

bar = Foo.new("fuga")
#=>"fuga"

アクセスメソッド

Rubyではオブジェクトの外部からインスタンス変数を直接参照したり、値を代入したりすることはできません。そのためオブジェクトの内部の情報にアクセスするメソッドを定義する必要がありますが、それをアクセスメソッドと言います。Javaでいうgetterとsetterのような感じです。

class Foo
    @name
    def name
        @name
    end
    def name=(value)   # 呼び出しは`~.name = "hogs"`でOK
        @name = value
    end
end

しかし、上記のように全て定義するのは大変なので、以下のようにインスタンス変数を示すシンボルを指定すると、インスタンス変数も含めて自動で定義してくれます。

  • attr_reader :name参照のみ可能にする(nameメソッドを定義する)
  • attr_writer :name変更のみ可能にする(name=メソッドを定義する)
  • attr_accessor :name参照と変更の両方を可能にする(上記2つを定義する)

要はこのメソッドを使うと、上記の例がそのまま展開されるようなイメージです。また、定数にアクセスメソッドは使えません。

self変数について

インスタンスメソッドの中でメソッドのレシーバ自身を参照するには self という変数を使います。

def name
    @name
end
def hello
    puts "Hi, I am #{self.name}."
end

この場合は helloメソッドを呼び出した時のレシーバを参照することになります。また、レシーバを省略してメソッドを呼ぶと暗黙的に self をレシーバとするため、省略して#{name}としても nameメソッドが呼ばれることに変わりはありません。

ただし、この場合でも アクセスメソッドの setter を使いたい時にname = "hoge"と書いてしまうと、nameというローカル変数が作成されてしまうため、self.name = "hoge"としてレシーバを明示する必要があります。

クラスメソッド

インスタンスではなくてクラスそのものをレシーバとするメソッドで、Java でいう staticメソッドみたいなものです。インスタンスに対する操作ではなく、そのクラスに関連する操作のために使われます。

class << HelloWorld
    def hello(name)   # クラスメソッド
        ...
    end
    def hoge   # クラスメソッド
        ...
    end
end

HelloWorld.hello("task")
HelloWorld.hoge
class HelloWorld
    class << self
        def hello(name)   # クラスメソッド
            ...
        end
    end

    def hoge   # インスタンスメソッド
        ...
    end
end

HelloWorld.hello("task")
test = HelloWorld.new
test.hoge
class HelloWorld
    def HelloWorld.hello(name)   # クラスメソッド
        ...
    end
    def self.hoge   # クラスメソッド(クラス定義内なので self 使用)
        ...
    end
end

def HelloWorld.fuga   # クラスメソッド

メソッドの呼び出しを制限する

メソッドには3種類の呼び出し制限があります。

public

メソッドをインスタンスメソッドとして制限なしに使えるようにします。

private

メソッドをレシーバを指定して呼び出せないようにします。レシーバを省略した形式(関数形式)でしか呼べないのでインスタンスの外側から使えません。

protected

メソッドを同一のクラスであればインスタンスメソッドとして使えます。メソッドを外側から呼べないのは private と同じですが、レシーバに self のみ指定することができます。

メソッドの呼び出し制限を変更するには次のようにします。

class HelloWorld
    def hoge
        ...
    end
    public :hoge

    def fuga
        ...
    end
    protected :fuga

この他に、private とのみ記述して、それ以降に定義されるメソッドをまとめて private にすることもできます。また、何も指定せずに定義されたメソッドは public になりますが、initializeメソッドだけは private として定義されます。

クラスの継承について

すでに定義されているクラスを拡張して新しいクラスを作ることを継承といいます。継承によって新しく作られたクラスを「サブクラス」、もとになったクラスを「スーパークラス」といいます。

Rubyのすべてのクラスは BasicObject クラスのサブクラスですが、BasicObject は本当に最低限の機能であるため、通常のオブジェクトに必要なクラスは Object クラスとして定義されており、基本的にほぼすべてのクラスの親は Object と考えていいです。Object クラスは BasicObject クラスを継承し、Kernel モジュールをインクルードしています。

サブクラスとスーパークラスの関係を調べるには、.is_a?(klass)メソッドか、.kind_of?(klass)メソッドを使います。

ary = Array.new()
p ary.is_a?(Object)   # ArrayクラスはObjectクラスのサブクラスのためtrue
#=>true
p ary.kind_of?(BasicObject)   # ArrayクラスはBasicObjectクラスのサブクラスのためtrue
#=>true
p ary.instance_of?(Object)   # Objectクラスのインスタンスではないためfalse
#=>false

instance_of?(klass)メソッドの場合はあるクラスのインスタンスかどうかしか判断できませんが、is_a?(klass.kind_of?(klass)は継承関係をさかのぼって判断してくれます。

クラスを継承する

クラスを継承するには次のように記述します。

class クラス名 < スーパークラス名
    クラスの定義
end

メソッドにはスーパークラスの同名メソッドを再定義することもできます。元のメソッドを呼び出したい時は super を使います。

class MyString < String
    def eql?(str)   # eql?()メソッドの再定義
        ...
        super(str)   # スーパークラスのeql?メソッドを呼ぶ
    end
end

継承ではないですが既存のクラスにメソッドを追加することもできます。

class String
    def hoge
        ...
    end
end

"test".hoge