クラス/メソッドの定義

クラス定義

例:

class Foo < Super
  def test
     :
  end
     :
end

文法:

class 識別子 [`<' superclass ]
  式..
end

文法:ruby 1.7 feature

class 識別子 [`<' superclass ]
  式..
[rescue [error_type,..] [=> evar] [then]
  式..]..
[else
  式..]
[ensure
  式..]
end

クラスを定義します。クラス名はアルファベットの大文字で始まる識別子です。 ruby 1.7 feature: version 1.7 では、rescue/ensure 節を指定で きます。

クラス定義は、識別子で指定した定数へのクラスの代入になります (Ruby では、クラスもオブジェクトの一つで Classクラスの インスタンスです)。

クラスが既に定義されているとき、さらに同じクラス名でクラス定義を書くとク ラスの定義の追加になります。ただし、元のクラスと異なるスーパークラスを 明示的に指定して定義すると、元のクラスとは異なる新たなクラスを同名で定 義することになります(このとき、クラス名の定数を上書きすることになるの で警告メッセージが出ます)。

class Foo < Array
  def foo
  end
end

# 定義を追加(スーパークラス Array を明示的に指定しても同じ)
class Foo
  def bar
  end
end

# 別のクラスを定義(スーパークラスが異なるので)
class Foo < String
end
# => warning: already initialized constant Foo

クラス定義式の中は self がそのクラスであることと、 呼び出し制限のデフォルトが異なること以外 にトップレベルとの違いはありません。クラス定義式中には任意の式を書くこ とができクラス定義の際に実行されます。

クラス定義はネスト(入れ子)にして定義できます。以下の例で入れ子の外側の クラス Foo と内側のクラス Bar の間には(定数 Bar が Foo の中の定数 Foo::Bar であること以外には)継承関係などの機能的な関連はまったくありま せん。

class Foo
  class Bar
  end
end

ruby 1.8 feature: クラス Foo が既に定義されていれば、以下の書き 方もできます。

class Foo
end

class Foo::Bar
end

クラスのネストは、意味的に関連するクラスを外側のクラス/モジュールでひ とまとまりにしたり、包含関係を表すために使用されます。

# 関連するクラスを Net というカテゴリにまとめる
# このような場合は外側は普通モジュールが利用される
# (Net のインスタンスがない。Net を include できるなどのため)
module Net
  class HTTP
  end
  class FTP
  end
end

obj = Net::HTTP.new

# あるいは

include Net
obj = HTTP.new

# 以下のような使い方は組み込みのクラスにも見られる
# 利用者は File::Constants を include することで、
# File::RDONLY などと書かずに直接 RDONLY と書くことができる。
class File
  module Constants
     RDONLY = 0
     WRONLY = 1
  end
  include Constants
end

File.open("foo", File::RDONLY)

# あるいは

include File::Constants
File.open("foo", RDONLY)

# 上記はあくまでも例である。実際の File.open ではより簡便な 
# File.open("foo", "r") という形式が使われる

クラス定義式は値を返しません。 ruby 1.7 feature: クラス定義式は、最後に評価した式の結果を返します。最後に評価した式 が値を返さない場合は nil を返します。

特異クラス定義

例:

obj = Object.new # obj = nil でも可
class << obj
  def test
     :
  end
     :
end

文法:

class `<<' expr
  式..
end

文法:ruby 1.7 feature

class `<<' expr
  式..
[rescue [error_type,..] [=> evar] [then]
  式..]..
[else
  式..]
[ensure
  式..]
end

クラス定義と同じ構文で特定のオブジェクトにメソッドやインスタンス変数を 定義/追加します。この構文の内部で定義したメソッドや定数は指定した オブジェクトに対してだけ有効になります。 Object#clone で生成したオブジェクトには引き継がれますが, Object#dup で生成したオブジェクトには引き継がれません. ruby 1.7 feature: version 1.7 では、rescue/ensure 節を指定で きます。

特異クラス定義式は、最後に評価した式の結果を返します。最後に評価した式 が値を返さない場合は nil を返します。

モジュール定義

例:

module Foo
  def test
     :
  end
     :
end

文法:

module 識別子
  式..
end

文法:ruby 1.7 feature

module 識別子
  式..
[rescue [error_type,..] [=> evar] [then]
  式..]..
[else
  式..]
[ensure
  式..]
end

モジュールを定義します。モジュール名はアルファベットの大文字 で始まる識別子です。 ruby 1.7 feature: version 1.7 では、rescue/ensure 節を指定で きます。

モジュール定義は、識別子で指定した定数へのモジュールの代入になります (Ruby では、モジュールもオブジェクトの一つで Module クラスのイン スタンスです)。

モジュールが既に定義されいるとき、さらに同じモジュール名でモジュール定 義を書くとモジュールの定義の追加になります。

モジュール定義式は値を返しません。 ruby 1.7 feature: モジュール定義式は、最後に評価した式の結果を返します。最後に評価した式 が値を返さない場合は nil を返します。

メソッド定義

例:

def fact(n)
  if n == 1 then
     1
  else
    n * fact(n-1)
  end
end

文法:

def メソッド名 [`(' [arg0 ['=' default0]] ... [`,' `*' rest_args] [',' '&' block_arg]`)']
  式..
[rescue [error_type,..] [=> evar] [then]
  式..]..
[else
  式..]
[ensure
  式..]
end

この定義のある場所にメソッドを定義します。すなわち、クラス/モジュール 定義中ならばそのクラス/モジュールのメソッドを定義します。トップレベル ならばどこからでも呼べるメソッドを定義します。このようなメソッドは結果 として他の言語における「関数」のように使えます。

例:

def hello    # 引数のないメソッド。
  puts "Hello, world!"
end

def foo(a, b)    # 引数のあるメソッド。括弧を省いてdef foo a, bとも
  a + 3 * b
end

メソッド名としては通常の識別子の他に、再定義可能な演算子(例: ==, +, - など 演算子式 を参照)も指定できます。

例:

class Vector2D
  attr_accessor :x, :y   # インスタンス変数@x, @yに対応するゲッタとセッタを定義
  def initialize(x, y)   # コンストラクタ
    @x = x; @y = y   # @がつくのがインスタンス変数(メンバ変数)
  end
  def ==(other_vec)   # いわゆる演算子オーバーライド
    other_vec.x == @x && other_vec.y == @y
  end
  def +(other_vec)
    Vector2D.new(other_vec.x + @x, other_vec.y + @y)
  end
  ...
end
vec0 = Vector2D.new(10, 20); vec1 = Vector2D.new(20, 30)
p vec0 + vec1 == Vector2D.new(30, 50) #=> true

仮引数にデフォルト式が与えられた場合、メソッド呼び出しで実引数を省略し たときのデフォルト値になります。 ただし実引数との対応を取るため、i番目の引数にデフォルト値を指定したならば、 i+1番目以降でも全てデフォルト値を指定するか、可変長引数を利用しなければなりません(詳細は後述)。 デフォルト式の評価は呼び出し時にメソッド定義内のコンテキストで行われます。

例:

def foo(x, y = 1)    # 2番目の引数yにデフォルト値を指定
  10 * x + y
end
p foo(1, 5)  #=> 15
p foo(3)     #=> 31
p foo        #=> ArgumentError (wrong number of arguments)

$gvar = 3
def bar(x, y = $gvar)  # 確かに定義時には$gvar == 3だが
  10 * x + y
end
$gvar = 7
# 呼び出し時の$gvarの値が使われる
p bar(5)   #=> 57 (!= 53)

最後の仮引数の直前に * がある場合には残りの実引数はみな配列とし てこの引数に格納されます。 可変長引数などと呼ばれる機能です。

例:

def foo(x, *xs)
  puts "#{x} : #{xs.inspect}"   # Object#inspect は p のような詳細な内部表示
end
foo(1)        #=> 1 : []
foo(1, 2)     #=> 1 : [2]
foo(1, 2, 3)  #=> 1 : [2, 3]

最後の仮引数の直前に & があるとこのメソッドに与えられているブロッ クが手続きオブジェクト(Proc)としてこの引数に格納されます。これは、 イテレータを定義する方法の一つです。(イテレータを 定義する代表的な方法は yield を呼び出すことです。 他に Proc.new/proc を使う方法などもありま す。) ブロックが与えられなかった場合のブロック引数の値はnilです。

例:

def foo(cnt, &block_arg)
  cnt.times { block_arg.call } # ブロックに収まったProcオブジェクトはcallで実行
end
foo(3) { print "Ruby! " } #=> Ruby! Ruby! Ruby! 

メソッド定義において、仮引数はその種類毎に以下の順序でしか指定すること はできません。いずれも省略することは可能です。

例:

# すべて持つ
def foo(arg0, arg1, arg2 = 10, *rest, &block)
  block.call if block
  puts "#{arg0}: #{arg1}: #{arg2}?: #{rest.inspect}"
end
foo(1, 2, 3, 4, 5) { print "Args are " }  #=> Args are 1: 2: 3?: [4, 5]

例: イテレータの定義

# yield を使う
def foo
# block_given? は、メソッドがブロックを渡されて
# 呼ばれたかどうかを判定する組み込み関数
  if block_given?
    yield(1,2)
  end
end

# Proc.new を使う
def bar
  if block_given?
    Proc.new.call(1,2)    # proc.call(1,2) でも同じ(proc は組み込み関数)
  end
end

# 応用: 引数として Proc オブジェクトとブロックの
# 両方を受け付けるイテレータを定義する例
def foo(block = Proc.new)
  block.call(1,2)
end
foo(proc {|a,b| p [a,b]})
foo {|a,b| p [a,b]}

# ブロック引数を使う
def baz(&block)
  if block
    block.call(1,2)
  end
end

そのほか特殊な形式をとるメソッド定義を以下に挙げます。

# 単項プラス/マイナス
def +@
def -@

# 要素代入
def foo=(value)             # obj.foo = value

# [] と []=
def [](key)                 # obj[key]
def []=(key, value)         # obj[key] = value
def []=(key, key2, value)   # obj[key, key2] = value

# バッククォート記法
def `(arg)                  # `arg` または %x(arg)

バッククォート記法の実装はメソッドなのでこのように再定義が可能です。普 通はこのメソッドを再定義するべきではありませんが、まれにOS(シェル)のコ マンド実行の挙動に不具合がある場合などに利用できます*1

またメソッド実行時の例外を捕捉するために begin 式と同様 のrescue, else, ensure 節を指定できます。

メソッド定義式は値を返しません。 ruby 1.7 feature: メソッド定義式は、nil を返します。

メソッド定義のネスト

特異メソッド定義を除くメソッド定義式は ネストできません。

ruby 1.7 feature: 1.7 以降ネスト可能になりました。ネストされた定 義式はそれを定義したメソッドが実行された時に定義されます。このことを除 けば、普通のメソッド定義式と同じです。以下の例を参照してください。 *2

class Foo
  def foo
    def bar
      p :bar
    end
  end

  def Foo.method_added(name)
    puts "method \"#{name}\" was added"
  end
end
obj = Foo.new
obj.bar rescue nil # => undefined method `bar' for #<Foo:0x4019eda4>
obj.foo            # => method "foo" was added
obj.foo            # => warning: method redefined; discarding old bar
Foo.new.bar        # => :bar  (他のインスタンスでも定義済み)

version 1.6 以前は、同じことを行うのに instance_eval を使 う必要がありました(この場合特異メソッドが定義されるので少し異なります)。

class Foo
  def foo
    instance_eval <<-END
      def bar
        p :bar
      end
    END
  end
end

obj = Foo.new
def obj.singleton_method_added(name)
    puts "singleton method \"#{name}\" was added"
end                # => singleton method "singleton_method_added" was added

obj.bar rescue nil # => undefined method `bar' for #<Foo:0x4019eda4>
obj.foo            # => singleton method "bar" was added

obj.foo            # => warning: method redefined; discarding old bar
                   # => singleton method "bar" was added
Foo.new.bar        # => undefined method `bar' for #<Foo:0x4019eda4>

または、以下のように書くこともできます。*3

class Foo
   def foo
     instance_eval {
       def bar
         p :bar
       end
     }
   end
 end

メソッドの評価

メソッドが呼び出されると、以下の順で式が評価されます。

引数のデフォルト式も含め、すべてそのメソッドのコンテキストで評価されま す。

メソッドの戻り値は return に渡した値です。return が呼び出されなかった場合は、メソッドの本体から ensure 節実行直前 までの最後に評価した式の値を返します。

最後に評価した式が(whileなど)値を返さない式の場合は nil を返します。

またメソッドは定義する前に呼び出すことはできません。例えば

foo
def foo
  print "foo\n"
end

は未定義メソッドの呼び出しで例外 NameError を発生させます。

特異メソッド定義

例:

def foo.test
  print "this is foo\n"
end

文法:

def 式 `.' 識別子 [`(' [引数 [`=' default]] ... [`,' `*' 引数 ]`)']
  式..
[rescue [error_type,..] [=> evar] [then]
  式..]..
[else
  式..]
[ensure
  式..]
end

特異メソッドとはクラスではなくある特定のオブジェクトに固有の メソッドです。特異メソッドの定義はネストできます。

クラスの特異メソッドはそのサブクラスにも継承されます。言い替 えればクラスの特異メソッドは他のオブジェクト指向システムにお けるクラスメソッドの働きをすることになります。

特異メソッド定義式は値を返しません。 ruby 1.7 feature: 特異メソッド定義式は、nil を返します。

クラスメソッドの定義

Ruby におけるクラスメソッドとはクラスの特異メソッドのことです。Ruby で は、クラスもオブジェクトなので、普通のオブジェクトと同様に特異メソッド を定義できます。

したがって、何らかの方法でクラスオブジェクトにメソッドを定義すれば、そ れがクラスメソッドとなります。具体的には以下のようにして定義することが 出来ます(モジュールも同様です)。

# 特異メソッド方式。
class Hoge
  def Hoge.foo
  end
end

# クラス定義の外でも良い
def Hoge.bar
end

# 以下のようにすればクラス名が変わってもメソッド部の変更が不要
class Hoge
  def self.baz
    'To infinity and beyond!'
  end
end

# 特異クラス方式。複数のメソッドを一度に定義するとき向き
class << Hoge
  def bar
    'bar'
  end
end

# モジュールをクラスに extend すれば、モジュールのインスタンス
# メソッドがクラスメソッドになる
module Foo
  def foo
  end
end
class Hoge
  extend Foo
end

extend については、Object#extend を参照して ください。

呼び出し制限

メソッドは publicprivateprotected の三通りの 呼び出し制限を持ちます。

例: protected の可視性

class Foo
  def foo
   p caller.last
  end
  protected :foo
end

obj = Foo.new

# そのままでは呼べない
obj.foo rescue nil    # => -:11 - private method `foo' called for #<Foo:0x401a1860> (NameError)

# クラス定義内でも呼べない
class Foo
  Foo.new.foo rescue nil # => -:15 - protected method `foo' called for #<Foo:0x4019eea8>
  # メソッド定義式内で呼べる
  def bar
    self.foo
  end
end
Foo.new.bar             # => ["-:21"]

# 特異メソッド定義式内でも呼べる
def obj.bar
  self.foo rescue nil
end
obj.bar                 # => ["-:27"]

デフォルトでは def 式がクラス定義の外(トップレベル)にあれば private、クラス定義の中にあれば public に定義します。これは Module#publicModule#privateModule#protected を用いて変更できます。ただし initialize という名前のメソッドと initialize_copy(ruby 1.8 feature)という名前のメソッ ドは定義する場所に関係なく常に private になります。

例:

def foo           # デフォルトは private
end

class C
  def bar         # デフォルトは public
  end

  def ok          # デフォルトは public
  end
  private :ok     # …だが、ここで private に変わる

  def initialize  # initialize は private
  end
end

privateprotected は同じ目的(そのメソッドを隠し外から呼 べないようにする)で使用されますが、以下のような例では、private は使えず、protected を利用する必要があります。 *4

class Foo
  def _val
    @val
  end
  protected :_val

  def op(other)

    # other も Foo のインスタンスを想定
    # _val が private だと関数形式でしか呼べないため
    # このように利用できない

    self._val + other._val
  end
end

定義に関する操作

alias

例:

alias foo bar
alias :foo :bar
alias $MATCH $&

文法:

alias 新メソッド名 旧メソッド名
alias 新グローバル変数名 旧グローバル変数名

メソッドあるいはグローバル変数に別名をつけます。メソッド名に は識別子そのものか Symbol を指定します(obj.method のよ うな式を書くことはできません)。alias の引数はメソッド 呼び出し等の一切の評価は行われません。

メソッドの定義内で別名を付けるにはModuleクラスのメソッド Module#alias_method を利用して下さい。

別名を付けられたメソッドは、その時点でのメソッド定義を引き継 ぎ、元のメソッドが再定義されても、再定義前の古いメソッドと同 じ働きをします。あるメソッドの動作を変え、再定義するメソッド で元のメソッドの結果を利用したいときなどに利用されます。

# メソッド foo を定義
def foo
  "foo"
end

# 別名を設定(メソッド定義の待避)
alias :_orig_foo :foo

# foo を再定義(元の定義を利用)
def foo
  _orig_foo * 2
end

p foo  # => "foofoo"

グローバル変数の alias を設定するとまったく同じ変数が定義されます。こ のことは一方の変数への代入は他方の変数にも反映されるようになることを意 味します。 添付ライブラリの importenv.rb はこのことを利用して組み込み変数 に英語名をつけます。 ruby 1.7 feature: version 1.6 では、グローバル変数の別名づけは、 特定の組み込み変数だけが対象でした。version 1.7 ではこの制限は取り除か れています。

# 特殊な変数のエイリアスは一方の変更が他方に反映される
$_ = 1
alias $foo $_
$_ = 2
p [$foo, $_]   # => [2, 2]

# こちらは通常の変数のエイリアスで本当の意味での
# エイリアスにはならない。これは、version 1.6 ま
# での制限
$bar = 3
alias $foo $bar
$bar = 4
p [$foo, $bar] # => [3, 4]

ただし、正規表現の部分文字列に対応する変数 $1,$2, ... には別名を付けることができません。ま た、インタプリタに対して重要な意味のあるグローバル変数 (組み込み変数を参照)を再定義すると動作に支障を来す場合が あります。

alias 式は nil を返します。

undef

例:

undef bar

文法:

undef メソッド名[, メソッド名[, ...]]

メソッドの定義を取り消します。メソッド名には識別子そのもの か Symbol を指定します(obj.method のような式を書くことはできません)。 undef の引数はメソッド呼び出し等の一切の評価は行われません。

メソッドの定義内で定義を取り消すにはModuleクラスのメソッ ド Module#undef_method を利用して下 さい。

undef のより正確な動作は、メソッド名とメソッド定義との 関係を取り除き、そのメソッド名を特殊な定義と関連づけます。この状態の メソッドの呼び出しは例えスーパークラスに同名のメソッドがあっても例外 NameError を発生させます。 (一方、メソッド Module#remove_method は、関係を取 り除くだけです。この違いは重要です)。

aliasによる別名定義と undefによる定義取り消しによってクラスのインタフェー スをスーパークラスと独立に変更することができます。ただし、メ ソッドがselfにメッセージを送っている場合もあるので、よく注意 しないと既存のメソッドが動作しなくなる可能性があります。

undef 式は nil を返します。

defined?

例:

defined? print
defined? File.print
defined?(foobar)
defined?($foobar)
defined?(@foobar)
defined?(Foobar)

文法:

defined? 式

式が定義されていなければ、偽を返します。定義されていれば式の種別 を表す文字列を返します。

定義されていないメソッド、undef されたメソッド、 Module#remove_method により削除さ れたメソッドのいずれに対しても defined? は偽を返します。

特別な用法として以下があります。

defined? yield

yield の呼び出しが可能なら真(文字列 "yield")を返します。 block_given? と同様にメソッドがブロック付きで呼ばれたか を判断する方法になります。

defined? super

super の実行が可能なら真(文字列 "super")を返します。

defined? a = 1
p a # => nil

"assignment" を返します。実際に代入は行いませんがローカル変数は定義されます。

/(.)/ =~ "foo"
p defined? $&  # => "$&"
p defined? $1  # => "$1"
p defined? $2  # => nil

$&, $1, $2, などは直前のマッチの結果値が設定された場合だけ真を返します。

def Foo(a,b)
end
p defined? Foo       # => nil
p defined? Foo()     # => "method"
Foo = 1
p defined? Foo       # => "constant"

大文字で始まるメソッド名に対しては () を明示しなければ定数の判定 を行ってしまいます。

以下は、defined? が返す値の一覧です。


*1 実際の応用例が [ruby-talk:10006],[ruby-dev:12829]にあります
*2version 1.8.0 には、ネストして定義したメソッドが Object のインスタンス メソッドになるというバグがありました
*31.6 以前は、def 式内の def 式が parser で許されなかったのでこのように書くことはできませんでした
*4正確には、private には関数を定義する目的があるが、呼び 出し制限の目的でも(ここに挙げた制限があるにもかかわらず) protected よりは private が使われることの方が多いようだ



rubyist ML