前のページ 次のページ 目次

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 オブジェクトのインスタンス変数を参照できますか

インスタンス変数を参照するメソッドが定義されていれば,そのメソッドを 使って値を知ったり,値をセットしたりすることができます. Module#attrattr_readerattr_writerattr_accessorを参照してください. もちろん,自分でメソッドを定義して参照することもできます.

method=の形のメソッド定義は,method=の間に 空白をいれることができませんが,メソッド呼出しの時には,空白をいれる ことが可能ですし,+-などのメソッドが定義されていれば, +=-=などの自己代入も行えます.

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 破壊的メソッドとは何ですか

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

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
こんなことができるわけです.
前のページ 次のページ 目次