Ruby is the interpreted high-level programming language that supports various programming paradigms. It was designed with an emphasis on programming productivity and simplicity.
The majority of object-oriented programming languages, like Ruby, have a subclassing mechanism that enables us to develop new classes whose behavior is based on, but different from, that of an existing class. With the definitions of fundamental terms, we'll start this examination of subclassing. You're presumably already familiar with these concepts if you've programmed in a language like Java, C++, or something similar.
Subclassing & Inheritance in Ruby
Lets us understand Subclassing and inheritance in Ruby with an example given below.
A car Concept type, for instance, might have attributes for the name and manufacturer. Then, you might specify more precise Concept kinds for automobiles and aircraft. These might have their own unique set of characteristics, such as wing length for aircraft or gearbox type for cars, but they will also inherit the characteristics of the vehicle Concept type.
Most object-oriented programming languages, including Ruby, have a subclassing mechanism that lets us create new classes with behavior based on but different from an existing class.
A class's ability to extend or inherit from another class referred to as the superclass, can be specified when a class is defined. If the class Gem extends the class Ruby, then Gem is the superclass of Ruby and Ruby is the subclass of Gem. When defining a class, if you do not provide a superclass, your class will automatically extend Object. Every class, with the exception of Object, has a single superclass and any number of subclasses.
Definitions
Class Hierarchy
Classes can be structured in a tree form that we refer to as the Ruby class hierarchy since they can have several subclasses but only one superclass.
Object
The Object class is the root of this hierarchy, and every class inherits directly or indirectly from it.
Descendants
The subclasses of a class, as well as the subclasses of those subclasses, and so on recursively, are its descendants.
Ancestors
The superclass, the superclass of the superclass, and so on up to Object are all considered a class's ancestors.
Overriding Methods
By introducing new methods, we can give a newly defined class new behavior. But more crucially, by redefining inherited methods, we can modify the class's inherited behavior.
For instance, the Object class includes a to_s method to convert an object to a string in a generic way.
o = Object.new
puts o.to_s # Prints something like "#<Object:0xb7f7fce4>"
One of the key concepts in object-oriented programming and subclassing is that when methods are called, the appropriate definition or redefinition of the method is found by looking them up dynamically. In other words, method invocations are looked up when they are executed rather than being bound statically at the time they are parsed.
Overriding Private Methods
It is not possible to call private methods from outside the class that defines them. However, they are passed down to subclasses. This implies that subclasses may call them and replace them.
When subclassing a class that you did not create, exercise caution; private methods are frequently used by classes as internal auxiliary methods. They are not intended to be visible and do not form a part of the class's public API. Even the names of the private methods that a class declares for its own usage won't be known to someone who hasn't seen the class's source code. You will unintentionally have overridden the internal utility method of the superclass if you declare a method (regardless of visibility) in your subclass that shares the same name as a private method in the superclass. This will almost likely result in unexpected results.
The conclusion is that you should only subclass in Ruby if you are familiar with how the superclass is implemented. You should not extend the functionality of a class by inheriting from it but rather by encapsulating and delegating to it if you just want to depend on the public API of the class and not on its implementation.
Augmenting Behavior by Chaining
Sometimes when we override a method, we merely want to add some additional code to enhance its behavior rather than completely replacing it. We need the means to call the overridden method from the overriding method in order to accomplish this. The keyword super is used to do this, which is referred to as chaining.
Invoking a method with the same name as the current one in the superclass of the current class is how super works, similar to a special method invocation. (Remember that the superclass does not have to define that method; it may inherit it from one of its ancestors.) Super allows you to specify arguments in the same way that you would for a regular method call. The initialize method of a class is a frequent and significant location for method chaining.
Inheritance and Class Variables
Given below is a representation of the base class and derived class.
As you can see, Class B is derived from Class A. Class A is the base class and Class B is a derived class.
A class and all of its subclasses share class variables. Subclass B may make use of a variable @@a that class A defines. Although this could initially seem like an Inheritance, it's truly something else. When we consider how to change the value of a class variable, the distinction becomes obvious.
If a subclass changes the value of a class variable that is already in use by a superclass, the superclass does not create a private duplicate of the class variable; instead, the subclass changes the value that is visible to it. Additionally, it changes how all other subclasses of the superclass see the shared value. If you run Ruby 1.8 with the -w flag, a warning about this is printed. This caution is not given in Ruby 1.9.
Any subclass can change the behavior of a class if it uses class variables and by altering the value of the common class variable and all its descendants. A strong argument, a justification for the substitution of class instance variables for class variables.
Demonstration of sharing of Class Variables
class A
@@value = 1 # A class variable
def A.value; @@value; end # An accessor method for it
end
print A.value # Display value of A's class variable
class B < A; @@value = 2; end # Subclass alters shared class variable
print A.value # Superclass sees altered value
class C < A; @@value = 3; end # Another alters shared variable again
print B.value # 1st subclass sees value from 2nd subclass
OUTPUT
123
Frequently Asked Questions
How do you inherit a class in Ruby?
Ruby supports single class inheritance, therefore a class can only inherit from another class; it cannot inherit from two superclasses. Ruby offers something called mixins that can be used to accomplish multiple inheritances.
Is there multiple inheritances in Ruby?
Ruby supports only single class inheritance, it does not support multiple class inheritance.
What is a nested hash?
We can use nested hashes to group or associate the data we're working with even more. They assist us in dealing with scenarios in which a category or piece of information is linked to a collection of values rather than a single discrete value.
What is the difference between a class and a module Ruby?
Methods and constants are gathered in modules. They are unable to produce instances. Classes have per-instance state and can produce instances (objects).
In Ruby, what does "!= nil" mean?
Nil is a unique value in Ruby that represents the absence of any value. Nil is a NilClass object. Nothing or void is referred to as nil in Ruby.
Conclusion
In this article, we have extensively discussed Subclassing and inheritance in Ruby.
We hope this blog has helped you enhance your knowledge regarding Subclassing and inheritance in Ruby.