[TOP][UP][<-PREV][NEXT->]

5. メソッド

5.1 オブジェクトにメッセージを送った時に実行されるメソッドはどのように捜されますか

特異メソッド、自クラスで定義されたメソッド、スーパークラス(Mix-inされた モジュールを含む。クラス名.ancestorsで表示される。)で定義されたメソッドの順に 最初に見つかったメソッドが実行されます。メソッドが見つからなかった 場合には、method_missingが同じ順で捜されます。

Module Indexed
  def [](n)
    to_a[n]
  end
end
Class String
  include Indexed
end
p String.ancestors # [String, Indexed, Enumerable, Comparable, Object, Kernel]
p "abcde".gsub!(/./, "\\&\n")[1]

は、残念ながら期待するように"b\n"を返してくれず、10を返してきます。 []がStringクラスで捜され、Indexedで定義されたものを捜し出す前に マッチしてしまうからです。Class Stringで直接[]を再定義すれば、 期待どおりになります。

5.2 +-は演算子ですか

+-などは演算子ではなくメソッド呼び出しです。したがって オーバーロードすることもできます。

class MyString < String
  def +(other)
    print super(other)
  end
end

ただし、以下のもの及びこれらを組み合わせたもの(!=、!~)は制御構造であり、 オーバーロードできません。

=, .., ..., !, not, &&, and, |, or, ~, ::

単項演算子をオーバーロード(もしくは定義)するには、メソッド名として +@-@を使います。

=は、インスタンス変数へのアクセスメソッドとして, クラス定義の中で次のようにメソッドを定義することができます。 また、+-なども定義することにより、+= などの自己代入演算も可能になります。

def attribute=(val)
  @attribute = val
end

5.3 関数はありますか

Rubyにおいて関数のように見えるものはすべてレシーバ(self)を省略した形の メソッドです。例えば

def writeln(str)
  print(str, "\n")
end

writeln("Hello, World!")

のように一見関数のように見えるものも、Objectクラスに定義された メソッドであり、隠されたレシーバーselfに送られているというわけです。 したがってRubyを純粋なオブジェクト指向言語と呼ぶことができます。

組込み関数のように、selfが何であっても同じ結果を返すメソッドは、 レシーバーを意識する必要がありませんので、関数と考えてもいいという ことになります。

5.4 オブジェクトのインスタンス変数を外から参照できますか

直接はできません。あらかじめそのオブジェクトにインスタンス変数を 参照するためのメソッド (アクセサと言います) を定義しておく必要が あります。たとえば以下のようにします。

class C
  def name
    @name
  end
  def name=(str)    # name の後に空白を入れてはいけない!
    @name = str
  end
end

c = C.new
c.name = 'やまだたろう'
p c.name                 #=> "やまだたろう"

またこのような単純なメソッド定義は Module#attrattr_readerattr_writerattr_accessor などを使って簡潔に行うことができます。 たとえば上にあったクラス定義は以下のように書き直せます。

class C
  attr_accessor :name
end

なんらかの理由でアクセスメソッドは作りたくないけれど参照はしたい場合は Object#instance_eval を使って参照することもできます。

5.5 privateprotectedの違いが分かりません

privateの意味は、メソッドを関数形式でだけ呼び出せるようにし、 レシーバー形式では呼び出せないようにするという意味です。したがって、 可視性がprivateなメソッドは、自クラス及びサブクラスからしか参照 できません。

protectedも同様に、自クラス及びサブクラスからしか参照できませんが、 関数形式でもレシーバー形式でも呼び出せます。

メソッドのカプセル化に必要な機能です。

5.6 インスタンス変数をpublicにしたいのですが

インスタンス変数をpublicにすることはデータのカプセル化という観点から見 て好ましくありませんので、Rubyではインスタンス変数へのアクセスはアクセス メソッドを使って行います。attrメソッドを使うことで外部からみると 変数にアクセスしているかのように振舞わせることが可能です。

class Foo
  def initialize(str)
    @name = str
  end
  attr("name")
  # これはこういうことです。
  # def name
  #   return @name
  # end
end

foo = Foo.new("Tom")
print foo.name, "\n"         # Tom

attr(name, public)で省略可能な二番目の引数にtrueを指定するこ とで書き込みメソッドを提供することも可能です。

class Foo
  def initialize(str)
    @name = str
  end
  attr("name", true)
  # これはこういうことです。
  # def name
  #   return @name
  # end
  # def name=(str)
  #   @name = str
  # end
end

foo = Foo.new("Tom")
foo.name = "Jim"
print foo.name, "\n"    # Jim

Module#attr_reader, attr_writer, attr_accessorも 参照してください。

5.7 メソッドの可視性を指定したいのですが

最初に断わっておくと、Rubyでは関数形式(レシーバを省略した形)でしか呼び 出すことのできないメソッドのことをprivateなメソッドと呼びます。ちょっ と変ってますね。

クラスのメソッドをprivateにすれば外部から呼び出すことができなくなりま す(ただしそのクラスのサブクラスからは呼び出すことができます)。クラス 内でしか呼び出すことのないメソッドはprivateにしておくとよいでしょう。

次のようにすればメソッドをprivateにすることができます。

class Foo
  def test
    print "hello\n"
  end
  private :test
end

foo = Foo.new
foo.test
#=> test.rb:9: private method `test' called for #<Foo:0x400f3eec>(Foo)

クラスメソッドをprivateにするにはprivate_class_methodを使います。

class Foo
  def Foo.test
    print "hello\n"
  end
  private_class_method :test
end

Foo.test
#=> test.rb:8: private method `test' called for Foo(Class)

同様にpublicpublic_class_methodを用いることでメソッドを publicにすることができます。

デフォルトでは、クラス内でのメソッド定義はinitializeを除いてpublic、 トップレベルではprivateになっています。

5.8 メソッド名に大文字で始まる識別子は使えますか

使えます。ただし、引数の無いメソッド呼出しに対して引数を括る()を省略できません。

5.9 superArgumentErrorになりますが

メソッド定義中でsuperと呼び出すと、引数がすべて渡されますので、 引数の数が合わないとArgumentErrorになります。異なる数の引数を 指定するには、super()に引数を指定してやります。

5.10 2段階上の同名のメソッドを呼びたいのですが

superは、1段上の同名のメソッドを呼び出します。それより上の同名の メソッドを呼び出すには、あらかじめそのメソッドをaliasしておきます。

5.11 組込み関数を再定義した時に、元の関数を呼びたい時はどうしますか

メソッド定義の中ではsuperが使えます。再定義する前にalias しておくと、元の定義が保たれます。 Kernelの特異メソッドとしても呼べます。

5.12 破壊的メソッドとは何ですか

オブジェクトの内容を変更してしまうメソッドで、文字列や配列、ハッシュ などにあります。同名のメソッドがあって、一方はオブジェクトのコピーを 作って返し、もう一方は変更されたオブジェクトを返すようになっている場合、 !のついた方が破壊的メソッドです。String#concatのように!がつかない メソッドでも破壊的なものはあります。

5.13 副作用が起こるのはどんな時ですか

実引数であるオブジェクトに対して、メソッドの中から破壊的メソッドを 適用した場合です.

def foo(str)
  str.sub!(/foo/, "baz")
end

obj = "foo"
foo(obj)
print obj
#=> "baz"

この場合、引数となったオブジェクトが変更されています。でも、これは、プログラム の中で必要があって副作用のあるメッセージをオブジェクトに対し て送っているので当たり前です。

5.14 メソッドから複数の戻り値を返すことはできますか

Rubyでは、メソッドの戻り値は一つしか指定できませんが、 配列を使うことによって、複数の戻り値を返すことができます。

return 1, 2, 3

とすると配列が返されます。つまり、

return [1, 2, 3]

とするのと同じです。

さらに多重代入を利用すると、複数の戻り値を戻すのとほとんど同じことがで きます。たとえば、

def foo
  return 20, 4, 17
end

a, b, c = foo
print "a:", a, "\n" #=> a:20
print "b:", b, "\n" #=> b:4
print "c:", c, "\n" #=> c:17

こんなことができるわけです。


[TOP][UP][<-PREV][NEXT->]