原文: https://zetcode.com/lang/rubytutorial/objects/

在 Ruby 教程的这一部分中,我们将简要介绍 Ruby 语言中对象的概念。 我们将在 OOP 一章中了解有关对象的更多信息。 我写了有关对象的本章,因为许多 Ruby 功能可能会使新手感到困惑,特别是如果他们已经知道任何其他编程语言的话。

Ruby 是一种面向对象的编程语言。 这意味着在 Ruby 程序中,我们使用对象。 从语言程序员的角度来看,Ruby 程序是令牌流。 这些标记是 Ruby 关键字,运算符,各种分隔符或字面值。 从语义的角度来看,Ruby 程序由对象组成。 这些对象是在 Ruby 脚本的生存期内创建和修改的。

有两种对象:内置对象和自定义对象。 内置对象是所有程序员都可以使用的预定义对象。 它们以 Ruby 语言的核心或各种库提供。 定制对象是由应用员为其应用域创建的。

必须先创建所有对象,然后才能使用它们。 我们经常使用术语对象实例化。 它是对象创建的同义词。 对象由数据和方法组成。 数据是对象的静态部分。 方法构成对象的动态部分。 对象被修改并通过方法彼此通信。

  1. #!/usr/bin/ruby
  2. puts "Ruby language"

我们有一个简单的 Ruby 脚本。 如果我们熟悉某些程序语言(例如 Pascal 或 C),我们可能会看到一个名为puts的关键字或函数及其参数"Ruby language"(它是一个字符串)。

Ruby 是一种纯粹的面向对象的语言,情况有所不同。 "Ruby language"确实是一个字符串,它是一种常见的数据类型。 但这也是一个对象。 与所有对象一样,我们可以调用其方法。 这与其他语言有点不同。 puts是一种方法。 方法是在对象中定义的函数。 方法本身并不存在。 实际上,puts方法是Kernel模块的一部分。

  1. #!/usr/bin/ruby
  2. Kernel.puts "Ruby language"
  3. Kernel.puts "Ruby language".size

在上面的脚本中,我们有两个代码行。

  1. Kernel.puts "Ruby language"

在第一个示例中,我们调用的puts方法没有Kernel部分,可以将其省略。 这样可以节省时间和一些打字。 实际上,这是Kernel.puts正式电话的简写。 在 C# 中,我们有Console.writeln在 Java 中System.println。 想法是一样的。 方法必须与某个对象关联,或者,如果是类方法,则必须与一个类关联。

  1. Kernel.puts "Ruby language".size

在此代码行中,我们将"Ruby language"字符串的大小打印到控制台。 这可能会使使用其他语言编写代码的程序员感到困惑。 在其他语言中,字符串是无法修改的原始数据类型,并且缺少其自己的方法。 在 Ruby 中,字符串是完整对象,并且具有自己的方法。 size方法就是其中一种。 它以字符为单位返回字符串的大小。

  1. $ ./simple2.rb
  2. Ruby language
  3. 13

代码示例的输出。

在下面的示例中,我们将看一个整数。 与字符串类似,整数值也是 Ruby 对象。

  1. #!/usr/bin/ruby
  2. puts 6.object_id
  3. puts 6.even?
  4. puts 6.zero?
  5. puts 6.class

在示例中,我们有一个整数 6。我们在数字上调用了一些方法。

  1. puts 6.object_id

6 是一个对象。 object_id是一种方法。 该方法返回与该对象关联的 ID。 每个对象都有一个 ID。 如果我们在对象上调用方法,则必须始终在两者之间放置点字符。

  1. puts 6.even?
  2. puts 6.zero?

在这里,我们在 6 个对象上调用两个方法。 如果数字为偶数,则even?返回true。 如果数字等于零,则zero?方法返回true。 请注意,这两种方法都以问号结尾。 这是 Ruby 约定。 返回布尔值的方法以问号结尾。

  1. puts 6.class

class方法告诉我们正在处理哪种对象。 在我们的例子中 6 是Fixnum

  1. $ ./objectnumber.rb
  2. 13
  3. true
  4. false
  5. Fixnum

代码示例输出。

Ruby 对象创建

我们已经提到必须先创建 Ruby 对象,然后才能使用它们。 可以隐式或显式创建对象。 隐式对象创建是通过字面值符号创建对象。 显式对象的创建通过使用new关键字进行。 始终使用new关键字创建自定义对象。 必须从特定的类创建自定义对象。 类是对象的模板。 一个类可以用来创建许多对象。

  1. #!/usr/bin/ruby
  2. class Being
  3. end
  4. puts 67
  5. puts "ZetCode"
  6. s = String.new "ZetCode"
  7. puts s
  8. # n1 = Fixnum.new 67
  9. # puts n1
  10. b = Being.new
  11. puts b

该代码示例演示了如何在 Ruby 中创建对象。

  1. class Being
  2. end

这是名为Being的自定义对象的模板。 使用class关键字创建模板。 自定义对象的模板通常放在源文件的顶部或单独的 Ruby 文件中。

  1. puts 67
  2. puts "ZetCode"

在这两行中,我们使用两个对象。 Fixnum类型的 67 对象和String类型的"ZetCode"字符串。 67 和"ZetCode"是我们所谓的字面值。 字面值是类型的特定值的字面值表示。 这两个对象是由 Ruby 解释器在后台创建的。 Ruby 中的某些对象是通过在源代码中指定其字面值来创建的。

  1. s = String.new "ZetCode"
  2. puts s

这是创建String对象的正式方法。 它等于之前使用字符串字面值的隐式创建。

  1. # n1 = Fixnum.new 67
  2. # puts n1

并非所有内置对象都可以使用new方法创建。 此代码无法编译。 到目前为止,Fixnum数字只能用字面值表示法创建。

  1. b = Being.new
  2. puts b

在这里,我们创建了自定义对象的实例。 puts方法为我们简要介绍了该对象。

  1. $ ./ocreation.rb
  2. 67
  3. ZetCode
  4. ZetCode
  5. #<Being:0x9944d9c>

示例的输出。

我们将继续进行一些正式的对象创建。

  1. #!/usr/bin/ruby
  2. s1 = String.new "Ruby"
  3. puts s1.size
  4. puts s1.downcase
  5. a1 = Array.new
  6. a1.push 1, 2, 3
  7. puts a1.include? 3
  8. puts a1.empty?
  9. r1 = Range.new 1, 6
  10. puts r1.class
  11. puts r1.include? 4

在示例中,我们创建了三个内置对象并调用了其中的一些方法。

  1. s1 = String.new "Ruby"
  2. puts s1.size
  3. puts s1.downcase

创建一个String对象。 我们调用对象的两种方法。 size方法返回字符串的大小。 downcase方法将字符串的字符小写。

  1. a1 = Array.new
  2. a1.push 1, 2, 3
  3. puts a1.include? 3
  4. puts a1.empty?

在这里,我们创建一个Array对象,并向其添加三个数字。 稍后我们调用两个数组方法。 include?方法检查特定值(在我们的例子中为 3)是否是数组的一部分。 empty?方法返回一个布尔值,指示该数组是否为空。

  1. r1 = Range.new 1, 6
  2. puts r1.class
  3. puts r1.include? 4

创建Range类的实例。 它包含从 1 到 6 的数字。class方法返回对象的名称。 include?方法检查数字 4 是否在范围内。 就我们而言。

  1. $ ./formal.rb
  2. 4
  3. ruby
  4. true
  5. false
  6. Range
  7. true

运行示例将给出此输出。

Ruby 对象字面值

正如我们已经提到的,可以使用对象字面值来创建一些内置对象。 以下示例显示了几个对象字面值。

  1. #!/usr/bin/ruby
  2. 4.times { puts "Ruby" }
  3. puts "Ruby".size
  4. puts "Ruby".downcase
  5. puts [1, 2, 3].include? 3
  6. puts [1, 2, 3].empty?
  7. puts :name.class
  8. puts :name.frozen?
  9. puts (1..6).class
  10. puts (1..6).include? 4

在上面的示例中,我们使用字面值符号创建一个Fixnum,字符串,数组,符号和范围。

  1. 4.times { puts "Ruby" }

我们可以立即在整数字面值上调用方法。 该行向终端打印四次"Ruby"字符串。

  1. puts "Ruby".size
  2. puts "Ruby".downcase

我们在用字符串字面值创建的String对象上调用两个方法。

  1. puts [1, 2, 3].include? 3
  2. puts [1, 2, 3].empty?

在这里,我们使用数组字面值符号创建两个Array对象。 我们使用include?方法检查特定的数字是否是数组的一部分。 empty?方法检查数组对象是否为空。

  1. puts :name.class
  2. puts :name.frozen?

调用 Symbol 对象的两种方法。 该符号是用一个以冒号开头的符号字面值创建的。

  1. puts (1..6).class
  2. puts (1..6).include? 4

使用范围字面值创建两个Range对象。 我们在那些对象上调用两个方法。 class方法返回类的名称,include?方法检查给定数字是否在范围内。

  1. $ ./literals.rb
  2. Ruby
  3. Ruby
  4. Ruby
  5. Ruby
  6. 4
  7. ruby
  8. true
  9. false
  10. Symbol
  11. false
  12. Range
  13. true

示例输出。

Ruby 对象层次结构

在许多面向对象的语言中,对象形成层次结构。 Ruby 也具有对象层次结构。 它是一个树状的层次结构,其中有父对象和子对象。 对象从其父对象继承数据和行为。 在层次结构的顶部,有根对象。 它称为Object。 Ruby 中的每个对象都有至少一个父对象。 换句话说,每个对象都继承自基本的Object对象。

根据 Ruby 的官方文档,Object是 Ruby 类层次结构的根。 除非明确重写,否则它的方法可用于所有类。

  1. #!/usr/bin/ruby
  2. puts 4.is_a? Object
  3. puts "Ruby".is_a? Object
  4. puts [2, 3].is_a? Object
  5. puts :name.is_a? Object
  6. puts (1..2).is_a? Object

在上面的代码示例中,我们演示了所有对象均从根Object继承

  1. puts 4.is_a? Object

我们使用is_a?方法检查数字是否为特定类型:换句话说,数字是否继承自给定的对象类型。

  1. $ ./mother.rb
  2. true
  3. true
  4. true
  5. true
  6. true

所有方法均返回true,这意味着所有对象均从母对象继承。

即使对于非常基本的 Ruby 对象,继承层次结构也可能非常复杂。

  1. #!/usr/bin/ruby
  2. puts 6.class
  3. puts 6.is_a? BasicObject
  4. puts 6.is_a? Object
  5. puts 6.is_a? Numeric
  6. puts 6.is_a? Integer
  7. puts 6.is_a? Fixnum
  8. puts 6.is_a? Bignum
  9. puts 6.is_a? String

在此示例中,我们对较小数值的继承层次结构有所了解。

  1. puts 6.class

我们找出数字值为 6 的对象是什么。该行将Fixnum打印到控制台。

  1. puts 6.is_a? BasicObject
  2. puts 6.is_a? Object
  3. puts 6.is_a? Numeric
  4. puts 6.is_a? Integer
  5. puts 6.is_a? Fixnum

以上所有行均返回true。 数字 6 是Fixnum。 从 Ruby 文档中,我们发现其他四个对象是Fixnum对象的父对象。

  1. puts 6.is_a? Bignum
  2. puts 6.is_a? String

以上两个对象不是 6 值的父对象。

  1. $ ./inheritance.rb
  2. Fixnum
  3. true
  4. true
  5. true
  6. true
  7. true
  8. false
  9. false

示例的输出。

我们将以一个示例结束本节,该示例演示自定义用户对象的继承。

  1. #!/usr/bin/ruby
  2. class Being
  3. def to_s
  4. "This is Being"
  5. end
  6. def get_id
  7. 9
  8. end
  9. end
  10. class Living < Being
  11. def to_s
  12. "This is Living"
  13. end
  14. end
  15. l = Living.new
  16. puts l
  17. puts l.get_id
  18. puts l.is_a? Being
  19. puts l.is_a? Object
  20. puts l.is_a? BasicObject

在示例中,我们创建两个对象BeingLivingLiving对象继承自Being。 第一个是父对象,第二个是子对象。

  1. class Being
  2. def to_s
  3. "This is Being"
  4. end
  5. def get_id
  6. 9
  7. end
  8. end

这是自定义 Ruby 对象的定义。 定义位于classend关键字之间。 在定义内部,我们创建两个方法。 当puts方法将对象作为参数时,它将调用其to_s方法。 它通常给出对象的字符串表示/描述。

  1. class Living < Being
  2. def to_s
  3. "This is Living"
  4. end
  5. end

我们创建Living对象的定义。 该对象继承自Being对象。 <运算符用于创建继承关系。 to_s方法被覆盖。

  1. l = Living.new

从上面的Living对象模板,我们创建Living对象的实例。 使用new关键字创建自定义对象的实例。

  1. puts l

puts方法调用Living对象的to_s方法。 如果Living类中未定义to_s方法,则将调用Being类中的to_s方法。

  1. puts l.get_id

Living对象未定义get_id方法。 在这种情况下,将检查父类是否存在这种方法。 在我们的情况下,Being方法具有这种方法,因此被称为。

  1. puts l.is_a? Being

该行返回trueLivingBeing的一种; 例如它继承自Being类。

  1. puts l.is_a? Object
  2. puts l.is_a? BasicObject

对于我们的Living自定义对象,我们没有明确指定与ObjectBasicObject对象的任何关系。 但是这两行返回true。 这是因为 Ruby 中的每个对象自动都是这两个对象的后代。 这是由 Ruby 解释器在后台完成的。

  1. $ ./custominher.rb
  2. This is Living
  3. 9
  4. true
  5. true
  6. true

Output of the example.

Ruby 顶级

Ruby 有一个特定的对象,称为 Ruby 顶级。 这是在任何其他上下文(例如类或模块定义)之外定义的默认执行环境。 顶级名称为main。 它是Object类型的实例。 有一个与main关联的局部空间,所有局部变量都驻留在该空间中。

  1. #!/usr/bin/ruby
  2. n1 = 3
  3. n2 = 5
  4. puts local_variables
  5. Kernel.puts self
  6. puts self.class

这是描述 Ruby 顶层的第一个示例。

  1. n1 = 3
  2. n2 = 5

在这里,我们定义了两个数值变量。 这些变量是本地变量。

  1. puts local_variables

在这里,我们列出了所有局部变量。 local_variablesKernel模块的一种方法,已混入每个Object(包括顶层对象)中。

  1. Kernel.puts self

self是 Ruby 伪变量。 它返回当前的对象接收者。 该行将"main"打印到控制台。 它是 Ruby 顶层的名称。 Kernel.puts代码的Kernel部分可以省略。 通过完全指定名称,我们显示puts方法属于Kernel模块。

  1. puts self.class

该行显示顶层的class。 我们得到顶层的对象类型。 它是Object,它是 Ruby 类层次结构的根。

  1. $ ./toplevel.rb
  2. n1
  3. n2
  4. main
  5. Object

这是示例的输出。 n1n2是与顶层关联的局部变量。 主要是为 Ruby 顶级执行环境指定的名称。 最后,Object是顶层的类型。

我们将有另一个与 Ruby 顶层相关的示例。

  1. #!/usr/bin/ruby
  2. @name = "Jane"
  3. @age = 17
  4. def info
  5. "#{@name} is #{@age} years old"
  6. end
  7. puts self.instance_variables
  8. puts self.private_methods.include? :info
  9. puts info

我们显示了属于顶级环境的实例变量和方法。

  1. @name = "Jane"
  2. @age = 17

我们定义了两个实例变量。 实例变量以 Ruby 中的@字符开头。 实例变量属于特定的对象实例。 在这种情况下,它们属于 Ruby 顶级。

  1. def info
  2. "#{@name} is #{@age} years old"
  3. end

这是一个方法定义。 每个方法必须属于某个对象。 此方法属于顶级对象。 所有顶级方法都是私有的。 私有方法的访问受到限制。

  1. puts self.instance_variables

instance_variables方法打印出self的所有实例变量,在此上下文中指向 Ruby 顶层。

  1. puts self.private_methods.include? :info

所有顶级方法都是自动私有的。 private_methods返回对象的所有私有方法。 由于方法很多,我们调用include?方法来检查info方法是否在其中。 请注意,我们通过符号名称来引用info方法。

  1. $ ./toplevel2.rb
  2. @name
  3. @age
  4. true
  5. Jane is 17 years old

示例输出。

本章介绍了 Ruby 语言中对象的一些基础知识。