May 12, 2021 Ruby
8. Class methods and class variables
11.. The inheritance of the class
13.. The operator is overloaded
Ruby is a pure object-oriented language, and everything in Ruby comes in the form of objects. E ach value in Ruby is an object, even the most primitive: strings, numbers, even true and false objects. T he class itself is also an object and is an instance of the Class class. This chapter will show you all the key object-oriented features associated with Ruby.
Class is used to specify the form of an object, which combines data notation and methods to organize the data into a neat package. The data and methods in the class are called members of the class.
When you define a class, you actually define a blueprint for a data type. This does not actually define any data, but rather what the name of the class means, that is, what makes up the object of the class and what can be done on that object.
The class definition starts with the keyword class, followed by the class name, and ends with an end separation to terminate the class definition. For example, we use the keyword class to define a Box class, as follows:
class Box code end
By convention, names must begin with capital letters, and if they contain more than one word, each word is capital, but there is no separator (ex: CamelCase).
The class provides a blueprint for the object, so basically, the object is created based on the class. W e use the new keyword to declare the object of the class. The following statement declares two objects of the class Box:
box1 = Box.new box2 = Box.new
The initialize approach is a standard Ruby class approach that works similarly to constructor in other object-oriented programming languages. W hen you want to initialize some class variables while creating an object, the initialize method comes in use. The method has a series of parameters that, like other Ruby methods, must be preceded by the def keyword, as follows:
class Box def initialize(w,h) @width, @height = w, h end end
Instance variables are class properties that become object properties when they are created with classes. T he properties of each object are assigned separately and do not share values with other objects. I nside the class, these properties are accessed using the operator, and outside the class, they are accessed using a common method called the Accessor method. Let's use the class Box defined above as an example, @width and @height as instance variables for the class Box.
class Box def initialize(w,h) # 给实例变量赋值 @width, @height = w, h end end
In order to use variables outside the class, we must define them inside the Accessor method, also known as the Geter method. The following example demonstrates the use of the accessor method:
#!/usr/bin/ruby -w # 定义类 class Box # 构造器方法 def initialize(w,h) @width, @height = w, h end # 访问器方法 def printWidth @width end def printHeight @height end end # 创建对象 box = Box.new(10, 20) # 使用访问器方法 x = box.printWidth() y = box.printHeight() puts "Width of the box is : #{x}" puts "Height of the box is : #{y}"Try it out . . .
When the code above executes, it produces the following results:
Width of the box is : 10 Height of the box is : 20
Similar to the accessor method used to access variable values, Ruby provides a way to set variable values outside the class, known as the Seter method, as defined as follows:
#!/usr/bin/ruby -w # 定义类 class Box # 构造器方法 def initialize(w,h) @width, @height = w, h end # 访问器方法 def getWidth @width end def getHeight @height end # 设置器方法 def setWidth=(value) @width = value end def setHeight=(value) @height = value end end # 创建对象 box = Box.new(10, 20) # 使用设置器方法 box.setWidth = 30 box.setHeight = 50 # 使用访问器方法 x = box.getWidth() y = box.getHeight() puts "Width of the box is : #{x}" puts "Height of the box is : #{y}"Try it out . . .
When the code above executes, it produces the following results:
Width of the box is : 30 Height of the box is : 50
The definition of an instance method, like the definition of other methods, uses the def keyword, but they can only be used through class instances, as shown in the following example. Their capabilities are not limited to accessing instance variables, but they can do more to meet your needs.
#!/usr/bin/ruby -w # 定义类 class Box # constructor method def initialize(w,h) @width, @height = w, h end # 实例方法 def getArea @width * @height end end # 创建对象 box = Box.new(10, 20) # 调用实例方法 a = box.getArea() puts "Area of the box is : #{a}"Try it out . . .
When the code above executes, it produces the following results:
Area of the box is : 200
A class variable is a variable that is shared in all instances of a class. I n other words, instances of class variables can be accessed by all object instances. The class variable is prefixed with two characters, and the class variable must be initialized in the class definition, as shown in the following example.
Class methods are defined using def self.methodname(), and class methods end with an end separator. Class methods can be called in the form of classname.methodname with the class name, as shown in the following example:
#!/usr/bin/ruby -w class Box # 初始化类变量 @@count = 0 def initialize(w,h) # 给实例变量赋值 @width, @height = w, h @@count += 1 end def self.printCount() puts "Box count is : #@@count" end end # 创建两个对象 box1 = Box.new(10, 20) box2 = Box.new(30, 100) # 调用类方法来输出盒子计数 Box.printCount()Try it out . . .
When the code above executes, it produces the following results:
Box count is : 2
Any class you define has a to_s method to return a string representation of the object. Here's a simple example of a Box object based on width and height:
#!/usr/bin/ruby -w class Box # 构造器方法 def initialize(w,h) @width, @height = w, h end # 定义 to_s 方法 def to_s "(w:#@width,h:#@height)" # 对象的字符串格式 end end # 创建对象 box = Box.new(10, 20) # 自动调用 to_s 方法 puts "String representation of box is : #{box}"Try it out . . .
When the code above executes, it produces the following results:
String representation of box is : (w:10,h:20)
Ruby gives you three levels of instance method protection: public, private, or protected. Ruby does not apply any access control on instances and class variables.
Here is a simple example that demonstrates the syntax of these three modifiers:
#!/usr/bin/ruby -w # 定义类 class Box # 构造器方法 def initialize(w,h) @width, @height = w, h end # 实例方法默认是 public 的 def getArea getWidth() * getHeight end # 定义 private 的访问器方法 def getWidth @width end def getHeight @height end # make them private private :getWidth, :getHeight # 用于输出面积的实例方法 def printArea @area = getWidth() * getHeight puts "Big box area is : #@area" end # 让实例方法是 protected 的 protected :printArea end # 创建对象 box = Box.new(10, 20) # 调用实例方法 a = box.getArea() puts "Area of the box is : #{a}" # 尝试调用 protected 的实例方法 box.printArea()Try it out . . .
When the above code is executed, it produces the following results. Here, the first method is called successfully, but the second method creates a problem.
Area of the box is : 200 test.rb:42: protected method `printArea' called for # <Box:0xb7f11280 @height=20, @width=10> (NoMethodError)
Inheritance is one of the most important concepts in object-oriented programming. Inheritance allows us to define a class based on another class, which makes it easier to create and maintain applications.
Inheritance helps reuse code and execute quickly, and unfortunately Ruby doesn't support multi-inheritance, but Ruby supports mixins. Mixin is like a specific implementation of multiple inheritance, in which only the interface part is inheritable.
When creating a class, the programmer can specify directly that the new class inherits members from an existing class, so that no new data members and member functions are written from the beginning. This existing class is called a base class or parent class, and the new class is called a derived class or sub-class.
Ruby also provides the concept of sub-classification, which is inheritance, which is explained in the following example. E xtending the syntax of a class is very simple. J ust add the name of the character and the parent class to the class statement. For example, the following defines the class BigBox as a sub-class of Box:
#!/usr/bin/ruby -w # 定义类 class Box # 构造器方法 def initialize(w,h) @width, @height = w, h end # 实例方法 def getArea @width * @height end end # 定义子类 class BigBox < Box # 添加一个新的实例方法 def printArea @area = @width * @height puts "Big box area is : #@area" end end # 创建对象 box = BigBox.new(10, 20) # 输出面积 box.printArea()
Try it out . . .
When the code above executes, it produces the following results:
Big box area is : 200
Although you can add new functionality to a derived class, sometimes you may want to change the behavior of methods that are already defined in the parent class. At this point, you can keep the method name the same, and overload the functionality of the method, as shown in the following example:
#!/usr/bin/ruby -w # 定义类 class Box # 构造器方法 def initialize(w,h) @width, @height = w, h end # 实例方法 def getArea @width * @height end end # 定义子类 class BigBox < Box # 改变已有的 getArea 方法 def getArea @area = @width * @height puts "Big box area is : #@area" end end # 创建对象 box = BigBox.new(10, 20) # 使用重载的方法输出面积 box.getArea()
Try it out . . .
We want to do vector addition of two Box objects using the plus operator, multiply the width and height of the Box by the operator, and use the umnational operator - reverse the width and height of the Box. Here's a version of the Box class with a mathematical operator definition:
class Box def initialize(w,h) # 初始化 width 和 height @width,@height = w, h end def +(other) # 定义 + 来执行向量加法 Box.new(@width + other.width, @height + other.height) end def -@ # 定义一元运算符 - 来对 width 和 height 求反 Box.new(-@width, -@height) end def *(scalar) # 执行标量乘法 Box.new(@width*scalar, @height*scalar) end end
Sometimes we want to prevent objects from being changed. I n Object, the freeze method does this by effectively turning an object into a constant. A ny object can be frozen by calling Object.freeze. Frozen objects cannot be modified, that is, you cannot change its instance variables.
You can use the Object.frozen? method to check whether a given object has been frozen. I f the object is frozen, the method returns true, otherwise a false value is returned. The following example explains the concept:
#!/usr/bin/ruby -w # 定义类 class Box # 构造器方法 def initialize(w,h) @width, @height = w, h end # 访问器方法 def getWidth @width end def getHeight @height end # 设置器方法 def setWidth=(value) @width = value end def setHeight=(value) @height = value end end # 创建对象 box = Box.new(10, 20) # 让我们冻结该对象 box.freeze if( box.frozen? ) puts "Box object is frozen object" else puts "Box object is normal object" end # 现在尝试使用设置器方法 box.setWidth = 30 box.setHeight = 50 # 使用访问器方法 x = box.getWidth() y = box.getHeight() puts "Width of the box is : #{x}" puts "Height of the box is : #{y}"Try it out . . .
When the code above executes, it produces the following results:
Box object is frozen object test.rb:20:in `setWidth=': can't modify frozen object (TypeError) from test.rb:39
You can define a constant inside a class by assigning a direct value or string value to a variable, and the definition of a constant does not require the use of . . . or . . By convention, the name of a constant is capitaled.
Once a constant is defined, you cannot change its value, and you can access the constant directly inside the class, just as you would a variable, but if you want to access the constant outside the class, you must use classname::constant, as shown in the following example.
#!/usr/bin/ruby -w # 定义类 class Box BOX_COMPANY = "TATA Inc" BOXWEIGHT = 10 # 构造器方法 def initialize(w,h) @width, @height = w, h end # 实例方法 def getArea @width * @height end end # 创建对象 box = Box.new(10, 20) # 调用实例方法 a = box.getArea() puts "Area of the box is : #{a}" puts Box::BOX_COMPANY puts "Box weight is: #{Box::BOXWEIGHT}"Try it out . . .
When the code above executes, it produces the following results:
Area of the box is : 200 TATA Inc Box weight is: 10
Class constants can be inherited or overloaded like instance methods.
There may be a situation where you want to create an object without calling the object constructor initialize, that is, creating an object using the new method, in which case you can call allocate to create an un initialized object, as shown in the following example:
#!/usr/bin/ruby -w # 定义类 class Box attr_accessor :width, :height # 构造器方法 def initialize(w,h) @width, @height = w, h end # 实例方法 def getArea @width * @height end end # 使用 new 创建对象 box1 = Box.new(10, 20) # 使用 allocate 创建两一个对象 box2 = Box.allocate # 使用 box1 调用实例方法 a = box1.getArea() puts "Area of the box is : #{a}" # 使用 box2 调用实例方法 a = box2.getArea() puts "Area of the box is : #{a}"Try it out . . .
When the code above executes, it produces the following results:
Area of the box is : 200 test.rb:14: warning: instance variable @width not initialized test.rb:14: warning: instance variable @height not initialized test.rb:14:in `getArea': undefined method `*' for nil:NilClass (NoMethodError) from test.rb:29
If class definitions are executable code, which means that they can be executed in the context of an object, self must reference something. Let's take a look at the following example: .
#!/usr/bin/ruby -w class Box # 输出类信息 puts "Type of self = #{self.type}" puts "Name of self = #{self.name}" endTry it out . . .
When the code above executes, it produces the following results:
Type of self = Class Name of self = Box
This means that the class definition can be executed by treating the class as the current object, and it also means that the method in the meta-class and the parent class is available during the method definition execution.