跳转至

Ruby 中的对象

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

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

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

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

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

#!/usr/bin/ruby

puts "Ruby language"

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

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

#!/usr/bin/ruby

Kernel.puts "Ruby language"
Kernel.puts "Ruby language".size

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

Kernel.puts "Ruby language"

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

Kernel.puts "Ruby language".size

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

$ ./simple2.rb
Ruby language
13

代码示例的输出。

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

#!/usr/bin/ruby

puts 6.object_id

puts 6.even?
puts 6.zero?

puts 6.class

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

puts 6.object_id

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

puts 6.even?
puts 6.zero?

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

puts 6.class

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

$ ./objectnumber.rb
13
true
false
Fixnum

代码示例输出。

Ruby 对象创建

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

#!/usr/bin/ruby

class Being
end

puts 67
puts "ZetCode"

s = String.new "ZetCode"
puts s

# n1 = Fixnum.new 67
# puts n1

b = Being.new
puts b

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

class Being
end

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

puts 67
puts "ZetCode"

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

s = String.new "ZetCode"
puts s

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

# n1 = Fixnum.new 67
# puts n1

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

b = Being.new
puts b

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

$ ./ocreation.rb
67
ZetCode
ZetCode
#<Being:0x9944d9c>

示例的输出。

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

#!/usr/bin/ruby

s1 = String.new "Ruby"
puts s1.size
puts s1.downcase

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

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

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

s1 = String.new "Ruby"
puts s1.size
puts s1.downcase

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

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

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

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

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

$ ./formal.rb
4
ruby
true
false
Range
true

运行示例将给出此输出。

Ruby 对象字面值

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

#!/usr/bin/ruby

4.times { puts "Ruby" }

puts "Ruby".size
puts "Ruby".downcase

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

puts :name.class
puts :name.frozen?

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

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

4.times { puts "Ruby" }

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

puts "Ruby".size
puts "Ruby".downcase

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

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

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

puts :name.class
puts :name.frozen?

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

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

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

$ ./literals.rb
Ruby
Ruby
Ruby
Ruby
4
ruby
true
false
Symbol
false
Range
true

示例输出。

Ruby 对象层次结构

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

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

#!/usr/bin/ruby

puts 4.is_a? Object
puts "Ruby".is_a? Object
puts [2, 3].is_a? Object
puts :name.is_a? Object
puts (1..2).is_a? Object

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

puts 4.is_a? Object

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

$ ./mother.rb 
true
true
true
true
true

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

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

#!/usr/bin/ruby

puts 6.class

puts 6.is_a? BasicObject
puts 6.is_a? Object
puts 6.is_a? Numeric
puts 6.is_a? Integer
puts 6.is_a? Fixnum

puts 6.is_a? Bignum
puts 6.is_a? String

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

puts 6.class

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

puts 6.is_a? BasicObject
puts 6.is_a? Object
puts 6.is_a? Numeric
puts 6.is_a? Integer
puts 6.is_a? Fixnum

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

puts 6.is_a? Bignum
puts 6.is_a? String

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

$ ./inheritance.rb
Fixnum
true
true
true
true
true
false
false

示例的输出。

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

#!/usr/bin/ruby

class Being

    def to_s
        "This is Being"
    end

    def get_id
        9
    end
end

class Living < Being

    def to_s
        "This is Living"
    end
end

l = Living.new

puts l
puts l.get_id
puts l.is_a? Being
puts l.is_a? Object
puts l.is_a? BasicObject

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

class Being

    def to_s
        "This is Being"
    end

    def get_id
        9
    end
end

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

class Living < Being

    def to_s
        "This is Living"
    end
end

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

l = Living.new

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

puts l

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

puts l.get_id

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

puts l.is_a? Being

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

puts l.is_a? Object
puts l.is_a? BasicObject

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

$ ./custominher.rb
This is Living
9
true
true
true

Output of the example.

Ruby 顶级

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

#!/usr/bin/ruby

n1 = 3
n2 = 5

puts local_variables

Kernel.puts self
puts self.class

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

n1 = 3
n2 = 5

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

puts local_variables

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

Kernel.puts self

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

puts self.class

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

$ ./toplevel.rb
n1
n2
main
Object

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

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

#!/usr/bin/ruby

@name = "Jane"
@age = 17

def info
   "#{@name} is #{@age} years old" 
end

puts self.instance_variables
puts self.private_methods.include? :info

puts info

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

@name = "Jane"
@age = 17

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

def info
   "#{@name} is #{@age} years old" 
end

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

puts self.instance_variables

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

puts self.private_methods.include? :info

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

$ ./toplevel2.rb
@name
@age
true
Jane is 17 years old

示例输出。

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



回到顶部