예배 규칙서 찾다
Ruby用户指南 3、开始 4、简单的例子 5、字符串 6、正则表达式 7、数组 8、回到那些简单的例子 9、流程控制 10、迭代器 11、面向对象思维 12、方法 13、类 14、继承 15、重载方法 16、访问控制 17、单态方法 18、模块 19、过程对象 20、变量 21、全局变量 22、实变量 23、局部变量 24、类常量 25、异常处理:rescue 26、异常处理:ensure 27、存取器 28、对象的初始化 29、杂项 RGSS入门教程 1、什么是RGSS 2、开始:最简单的脚本 3、数据类型:数字 4、数据类型:常量与变量 5、数据类型:字符串 6、控制语句:条件分歧语句 7、控制语句:循环 8、函数 9、对象与类 10、显示图片 11、数组 12、哈希表(关联数组) 13、类 14、数据库 15、游戏对象 16、精灵的管理 17、窗口的管理 18、活动指令 19、场景类 Programming Ruby的翻译 Programming Ruby: The Pragmatic Programmer's Guide 前言 Roadmap Ruby.new 类,对象和变量 容器Containers,块Blocks和迭代Iterators 标准类型 深入方法 表达式Expressions 异常,捕捉和抛出(已经开始,by jellen) 模块 基本输入输出 线程和进程 当遭遇挫折 Ruby和它的世界 Ruby和Web开发 Ruby Tk Ruby 和微软的 Windows 扩展Ruby Ruby语言 (by jellen) 类和对象 (by jellen) Ruby安全 反射Reflection 内建类和方法 标准库 OO设计 网络和Web库 Windows支持 内嵌文档 交互式Ruby Shell 支持 Ruby参考手册 Ruby首页 卷首语 Ruby的启动 环境变量 对象 执行 结束时的相关处理 线程 安全模型 正则表达式 字句构造 程序 变量和常数 字面值 操作符表达式 控制结构 方法调用 类/方法的定义 内部函数 内部变量 内部常数 内部类/模块/异常类 附加库 Ruby变更记录 ruby 1.6 特性 ruby 1.7 特性 Ruby术语集 Ruby的运行平台 pack模板字符串 sprintf格式 Marshal格式 Ruby FAQ Ruby的陷阱
문자

类/方法的定义

  • 类定义
  • 特殊类定义
  • 模块定义
  • 方法定义
    • 方法定义的嵌套
    • 方法的计算
  • 特殊方法定义
  • 类方法的定义
  • 调用限制
  • 与定义有关的操作
    • alias
    • undef
    • defined?

类定义

例:

class Foo < Super
  def test
     :
  end
     :
end

语法:

class 标识符 [`<' superclass ]
  表达式..
end

语法:ruby 1.7 特性

class 标识符 [`<' superclass ]
  表达式..
[rescue [error_type,..] [=> evar] [then]
  表达式..]..
[else
  表达式..]
[ensure
  表达式..]
end

用来定义类的内容。类名(标识符)由大写字母开头。ruby 1.7 特性:在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 特性:如果Foo类已经定义过了的话,还可以这么写。

class Foo
end

class Foo::Bar
end

类的嵌套就是指,把与类有关的类和模块放在该类的外侧,使它们构成一个整体,借以表达某种包含关系。

# 把与NET有关的类置入NET内部
# 常使用模块来作为嵌套的外侧部分
# (Net没有实例。这主要是为了能够包含(include)Net)
module Net
  class HTTP
  end
  class FTP
  end
end

obj = Net::HTTP.new

# 或者

include Net
obj = HTTP.new

# 下列用法在内部类中也可使用
# 使用者只要包含(include)了File::Constants
# 就可以直接使用RDONLY,而不必写File::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 特性:类定义表达式将返回最后被计算的式子的值。若最后的式子不返回值,就返回nil。

特殊类定义

例:

class << obj
  def test
     :
  end
     :
end

语法:

class `<<' expr
  表达式..
end

语法:ruby 1.7 特性

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

与类定义的语法结构相同,它定义特定对象的功能。在其内部定义的方法和常数只对该特定对象有效。ruby 1.7 特性:在version 1.7中,还可以使用rescue/ensure部分。

特殊类定义表达式将返回最后被计算的式子的值。若最后的式子不返回值,就返回nil。

模块定义

例:

module Foo
  def test
     :
  end
     :
end

语法:

module 标识符
  表达式..
end

语法:ruby 1.7 特性

module 标识符
  表达式..
[rescue [error_type,..] [=> evar] [then]
  表达式..]..
[else
  表达式..]
[ensure
  表达式..]
end

用来定义模块的内容。模块名(标识符)由大写字母开头。ruby 1.7 特性:在version 1.7中,还可以使用rescue/ensure。

模块定义实际上就是把模块赋值给由模块名指定的常数(在Ruby中,模块也是一个对象,它是Module类的实例)。

若某个模块已经被定义过,此时又用相同的模块名来定义模块的话,就意味着对原有的模块定义进行追加。

模块定义表达式没有返回值.ruby 1.7 特性:模块定义表达式将返回最后被计算的式子的值.若该式子不返回值,则返回nil.

方法定义

例:

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

语法:

def 方法名 [`(' [arg ['=' default]] ... [`,' `*' arg] [',' '&' arg]`)']
  表达式..
[rescue [error_type,..] [=> evar] [then]
  表达式..]..
[else
  表达式..]
[ensure
  表达式..]
end

在定义语句所在的区域内定义一个方法.也就是说,若在类/模块的定义部分内定义一个方法的话,该方法就属于这个类/模块.若在顶层定义了一个方法的话,您就可以在任何地方调用它.这种方法其实就是其他语言中所说的"函数".

方法名中,除了可以使用通常的标识符以外,还可以使用可重载的操作符(例:==,+,-等等.请参考操作符表达式).

若给形参指定了默认表达式的话,在方法调用过程中如果实参被省略时,该默认表达式的值就会变成默认值(方法调用时,在方法定义内计算默认表达式的值).

若最后一个形参的前面带"*"的话,所有剩下的实参将被转为数组后传递给该参数.

例:

# 没有参数的方法。以下省略 end
def foo
end

# 有参数的方法
def foo(arg, arg2)

# 有默认参数的方法
def foo(arg = nil)

# 带块
def foo(arg, &block)

# 参数一应俱全
def foo(arg, arg2, arg3 = nil, *rest, &block)

# 操作符表达式
def ==(other)
def +(other)
def *(other)

若最后一个形参前面带"&"的话,表示传递给该参数的块是一个过程对象(Proc).这是定义迭代器的一种方法.(定义迭代器的典型方法是调用yield.还可以使用Proc.new/proc等方法.)当没有给出块时,块参数的值为nil.

在方法定义中,只能以下列顺序指定形参.其中任何一项都是可选的.

  • 没有默认表达式的参数(可多选)
  • 有默认表达式的参数(可多选)
  • 带*的参数(只能有一个)
  • 带&的参数(只能有一个)

例: 定义迭代器

# 使用 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(SHELL)命令的运作不太正常时,可以使用这种方法.

为了捕捉在方法运行时发生的异常,可以使用同begin一样的rescue,else或ensure语句.

方法定义表达式不会返回值.ruby 1.7 特性:方法定义表达式返回nil.

方法定义的嵌套

除了特殊方法定义以外,方法定义表达式不能进行嵌套.

ruby 1.7 特性: 在1.7 以后的版本中,就可以进行嵌套了.只有嵌套外侧的方法被执行时,嵌套方法才会被定义.除此以外,它和普通的方法定义表达式没有区别.请参考下例.

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>

还可以这么写.

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

方法的计算

调用方法时,将按照下列顺序依此计算各个表达式.

  • 参数的默认表达式(若有的话)
  • 方法的内容
  • 根据发生异常的实际状况,处理方法定义表达式的rescue部分或else部分(若有的话)
  • ensure部分(若有的话)

在方法内,根据实际情况来计算这些部分,包括参数的默认表达式在内.

方法的返回值就是传给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 特性:特殊方法定义表达式返回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

请参考Object#extend来了解extend.

调用限制

调用方法时,会受到以下三种限制,即publicprivateprotected.

  • 若方法属于public类型,则没有任何限制.
  • 若方法属于private类型,则只能在函数中调用.
  • 若方法属于protected类型,则只能在该方法所属对象的方法定义表达式内使用.

例: 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#public,Module#privateModule#protected来改变它们的类型.但是,initialize方法和initialize_copy(ruby 1.8 特性)方法总是private类型,这与它们的位置无关.

例:

def foo           # 默认为 private
end

class C
  def bar         # 默认为 public
  end

  def ok          # 默认为 public
  end
  private :ok     # 变为 privat

  def initialize  # initialize 是 private
  end
end

使用privateprotected的目的是相同的(将对象隐藏起来,从外部不能调用).但是在下例中,不能使用private,而必须使用protected.

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 特性:在1.6版本中,只能给特定的内部全局变量添加别名.到了1.7版本时,这项限制被取消了.

# 在给特殊变量添加别名之后,当其中一个发生变化时,另一个也会有所反应
$_ = 1
alias $foo $_
$_ = 2
p [$foo, $_]   # => [2, 2]

# 这是通常的变量的别名,它并非真正意义上的别名.
# 这是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?的所有的返回值.

  • "super"
  • "method"
  • "yield"
  • "self"
  • "nil"
  • "true"
  • "false"
  • "assignment"
  • "local-variable"
  • "local-variable(in-block)"
  • "global-variable"
  • "instance-variable"
  • "constant"
  • "class variable"
  • "$&", "$`", "$1", "$2", ...
  • "expression"

이전 기사: 다음 기사: