Inheritance means that a new class can inherit methods from an existing class. The new class can then add to or modify existing code in order to customize the class without having to reinvent what has already been done. The principle is that a class may be subdivided into a number of subclasses that all share common features, but each subclass may provide its own additional features, refining what it borrows to a more specific functionality. The idea of this kind of organization is not new. You may have seen it in a biology class when learning about the plant and animal kingdoms and the breakdown of each phylum, kingdom, class, order, family, species, and variety or in procedural programs with the use of functions to combine the common elements of a program into specific tasks.
In object-oriented programming, once a class has been written and debugged, it can be stored in a library and reused by other programmers. The programmer can then add features and capabilities to the existing class without rewriting the whole thing. This is done through inheritance; that is, by deriving a new class from an already existing class. The reuse of software and the increased use of library classes where all this software is stored and organized have contributed to the wide popularity of OOP languages. Let's see how Perl implements inheritance.
The classes (packages) listed in the @ISA array are the parent, or base, classes of the current class. This is how Perl implements inheritance. The @ISA array contains a list of packages (classes) where Perl will search for a method if it can't find it in the current package (class). If the method still isn't found, then Perl searches for an AUTOLOAD function and calls that method instead. And if that isn't found, then Perl searches for the last time in a special predefined package called UNIVERSAL. The UNIVERSAL class is a global base class for all packages, the highest class in the hierarchy of classes.
The @ISA array is not searched in a call to a normal subroutine but in a call to a subroutine if it is called with the method invocation syntax.
#!/bin/perl # Example of attempting inheritance by updating the @ISA array 1 { package Grandpa; $name = "Gramps"; 2 sub greetme { print "Hi $Child::name I'm your $name from package Grandpa.\n"; } } 3 { package Parent; 4 @ISA=qw(Grandpa); # Grandpa is a package in the @ISA array. # This package is empty. } 5 { package Child; $name = "Baby"; 6 print "Hi I'm $name in the Child Package here.\n"; 7 Parent->greetme(); # Parent::greetme() will fail } (Output) 6 Hi I'm Baby in the Child Package here. 7 Hi Baby I'm your Gramps from package Grandpa. Explanation
|
If a subroutine (or method) cannot be found in the current package or in the @ISA array, the AUTOLOAD function will be called. The $AUTOLOAD variable is assigned the name of the missing subroutine if it is used with the AUTOLOAD function. Arguments passed to the undefined subroutine are stored in the AUTOLOAD subroutine's @_ array. If you assign a function name to the $AUTOLOAD variable, that subroutine will be called if the AUTOLOAD subroutine is provided in place of the missing subroutine. If the $AUTOLOAD variable is used with the AUTOLOAD subroutine, either the method or regular subroutine syntax can be used. If all fails and Perl still can't find the subroutine, a final package (class) called UNIVERSAL is searched for the missing method. The UNIVERSAL method contains three methods that all classes inherit. They are isa(), can(), and VERSION(). (See Table 14.2.)
Method | What It Does | Example |
---|---|---|
isa | Returns true if one package inherits from another. | Salesman–>isa("Employee"); |
can | Returns true if a package or any of its base classes contain a specified method. | Salesman–>can("get_data"); |
VERSION | Used to check that the correct modules are loaded for that version number. In the example, Perl calls the UNIVERSAL method Salesman–>VERSION(6.1). | package Salesman; use$VERSION=6.1; |
Code View: #!/bin/perl 1 { package Grandpa; $name = "Gramps"; sub greetme { 2 print "Hi $Child::name I'm your $name from package Grandpa.\n"; } } 3 { package Parent; 4 sub AUTOLOAD{ 5 print "$_[0]: $_[1] and $_[2]\n"; 6 print "You know us after all!\n"; 7 print "The unheard of subroutine is called $AUTOLOAD.\n" } } 8 { package Child; $name = "Baby"; 9 $AUTOLOAD=Grandpa->greetme(); 10 print "Hi I'm $name in the Child Package here.\n"; 11 Parent->unknown("Mom", "Dad"); # Undefined subroutine } (Output) 2 Hi Baby I'm your Gramps from package Grandpa. 10 Hi I'm Baby in the Child Package here. 5 Parent: Mom and Dad 6 You know us after all! 7 The unheard of subroutine is called Parent::unknown. Explanation
|
Code View: #!/bin/perl 1 { package Grandpa; $name = "Gramps"; 2 sub greetme { print "Hi $Child::name I'm your $name from package Grandpa.\n"; } } 3 { package Parent; # This package is empty } 4 { package Child; $name = "Baby"; 5 print "Hi I'm $name in the Child Package here.\n"; 6 Parent->greetme(); } 7 package UNIVERSAL; 8 sub AUTOLOAD { 9 print "The UNIVERSAL lookup package.\n"; 10 Grandpa->greetme(); } (Output) 2 Hi I'm Baby in the Child Package here. 9 The UNIVERSAL lookup package. 5 Hi Baby I'm your Gramps from package Grandpa. Explanation
|
As already discussed, inheritance is when one class can inherit methods from an existing class. The existing class is called the base, or parent, class, and the new class that inherits it is called the derived, or child, class. The base class has capabilities that all its derived classes inherit, and the derived class can then go beyond those capabilities. If a derived class inherits from one base class, it is called single inheritance. For example, single inheritance in real life might be that a child inherits his ability to draw from his father. If a derived class inherits from more than one base class, this is called multiple inheritance. To continue the analogy, the child inherits his ability to draw from his father and his ability to sing from his mother. In Perl, the derived class inherits methods from its base class (package) and can add and modify these methods when necessary.
The classes are inherited by putting them in the @ISA array. The methods to be exported to other modules can be specified in either the @EXPORTER or the @EXPORTER_OK arrays. The data itself is inherited by referencing keys and values in the anonymous hash where the data was initially assigned. These variables are called instance variables and are defined in the constructor method.
In Chapter 12, we looked at modules from the Perl standard library and modules you could create yourself. In order to include a module or pragma into your program, the use function was called with the module name (minus the .pm extension). The module had the capability to export symbols to other packages that might need to use the module. A special module called Exporter.pm handled the details for exporting and importing symbols between modules. This module also needs to be included in the @ISA array. If a module functions as a class, then its methods can be called without explicitly listing them in the @EXPORT array. Note in the following examples, the class methods and the instance methods are not exported.
The following examples demonstrate inheritance. Example 14.22 is the user of the Salesman.pm class. The user program need not make any reference to the base class, Employee.pm (see Example 14.23). The Salesman.pm class is derived from Employee.pm. The Salesman.pm class "uses" the Employee.pm class.
Code View: # The Driver (user) Program 1 use Salesman; 2 use strict; use warnings; # Create two salesman objects print "Entering data for the first salesman.\n"; 3 my $salesguy1=Salesman->new; 4 $salesguy1->set_data; 5 print "\nEntering data for the second salesman.\n"; 6 my $salesguy2=Salesman->new; 7 $salesguy2->set_data; 8 print "\nHere are the statistics for the first salesman.\n"; 9 $salesguy1->get_data; 10 print "\nHere are the statistics for the second salesman.\n"; 11 $salesguy2->get_data; (Output) The salesman is an employee. The salesman can display its properties. Entering data for the first salesman. Enter the name of the employee. Russ Savage Enter the address of Russ Savage. 12 Main St., Boston, MA Enter the monthly base pay for Russ Savage. 2200 Before blessing package is: Employee After blessing package is: Salesman Enter Russ Savage's commission for this month. 1200 Enter Russ Savage's bonuses for this month. 500.50 Enter Russ Savage's sales region. northeast 5 Entering data for the second salesman. Enter the name of the employee. Jody Rodgers Enter the address of Jody Rodgers. 2200 Broadway Ave, Chico, CA Enter the monthly base pay for Jody Rodgers. 34500 Before blessing package is: Employee After blessing package is: Salesman Enter Jody Rodgers's commission for this month. 2300 Enter Jody Rodgers's bonuses for this month. 1400 Enter Jody Rodgers's sales region. northwest 8 Here are the statistics for the first salesman. Name = Russ Savage. Bonuses = 500.50. Commission = 1200. BasePay = 2200. Address = 12 Main St., Boston, MA. PayCheck = 3900.5. Region = northeast. 10 Here are the statistics for the second salesman. Name = Jody Rodgers. Bonuses = 1400. Commission = 2300. BasePay = 34500. Address = 2200 Broadway Ave, Chico, CA. PayCheck = 38200. Region = northwest. Explanation
|
Code View: # Module Employee.pm # The Base Class 1 package Employee; 2 use strict; 3 use warnings; # Constructor method 4 sub new { 5 my $class = shift; 6 my $self = {_Name=>undef, _Address=>undef, _BasePay=>undef, }; 7 return bless($self, $class); } # Instance/access methods 8 sub set_data{ 9 my $self=shift; 10 print "Enter the name of the employee. "; 11 chomp($self->{_Name}=<STDIN>); print "Enter the address of $self->{_Name}. "; chomp($self->{_Address}=<STDIN>); print "Enter the monthly base pay for $self->{_Name}. "; chomp($self->{_BasePay}=<STDIN>); } 12 sub get_data{ 13 my $self=shift; 14 my ($key,$value); 15 print "Name = $self->{_Name}.\n"; 16 while(($key,$value)=each(%$self)){ 17 $key =~ s/_//; 18 print "$key = $value.\n" unless $key eq "Name"; } print "\n"; } 1; Explanation
|
Code View: # The Derived Class 1 package Salesman; 2 use strict; use warnings; 3 BEGIN{unshift(@INC, "./Baseclass");}; 4 our @ISA=qw( Employee); 5 use Employee; 6 print "The salesman is an employee.\n" if Salesman->isa('Employee'); 7 print "The salesman can display its properties.\n" if Salesman->can('get_data'); 8 sub new { # Constructor for Salesman 9 my ($class)= shift; 10 my $emp = new Employee; 11 $emp->set_data; 12 print "Before blessing package is: ", ref($emp), "\n"; bless($emp, $class); 13 print "After blessing package is: ", ref($emp), "\n"; return $emp; } 14 sub set_data{ my $self=shift; 15 my $calc_ptr = sub{ my($base, $comm, $bonus)=@_; return $base+$comm+$bonus; }; 16 print "Enter $self->{_Name}'s commission for this month. "; chomp($self->{_Commission}=<STDIN>); print "Enter $self->{_Name}'s bonuses for this month. "; chomp($self->{_Bonuses}=<STDIN>); print "Enter $self->{_Name}'s sales region. "; chomp($self->{_Region}=<STDIN>); 17 $self->{_PayCheck}=&$calc_ptr( $self->{_BasePay}, $self->{_Commission}, $self->{_Bonuses} ); } 1; Explanation
|
When a class inherits methods from more than one base, or parent, class, it is called multiple inheritance. In Perl, multiple inheritance is accomplished by adding more than one class to the @ISA array.
package Child; @ISA = qw (Mother Father Teacher);
The search is depth-first, meaning that Perl will search for classes in Mother and the hierarchy of classes it descends from, then Father and the hierarchy of classes it descends from, and finally Teacher and all its ancestors.
There are times when two classes may have a method with the same name. If a derived class has a method with the same name as the base class, its method will take precedence over the base method. To override the method in the derived class so you can access the method in the base class, the name of the method must be fully qualified with the class name and two colons.
Code View: 1 package Employee; # Base class use strict; use warnings; sub new { # Employee's constructor is defined my $class = shift; my %params = @_; my $self = { Name=>$params{"Name"}, Salary=>$params{"Salary"}, }; bless ($self, $class); } 2 sub display { # Instance method my $self = shift; foreach my $key ( @_){ 3 print "$key: $self->{$key}\n"; } 4 print "The class using this display method is ", ref($self),"\n"; } 1; --------------------------------------------------------------------- 5 package Salesman; # Derived class use strict; use warnings; use Employee; 6 our @ISA=qw (Exporter Employee); 7 sub new { # Constructor in derived Salesman class my $class = shift; my (%params) = @_; my $self = new Employee(%params); # Call constructor # in base class $self->{Commission} = $params{Commission}; bless ( $self, $class ); # Rebless the object into # the derived class } sub set_Salary { my $self = shift; $self->{Salary}=$self->{Salary} + $self->{Commission}; } 8 sub display{ # Override method in Employee class my $self = shift; my @args = @_; 9 print "Stats for the Salesman\n"; print "-" x 25, "\n"; 10 $self->Employee::display(@args); # Access to the # overridden method } 1; ----------------------------------------- # User or Driver Program #!/bin/perl 11 use Salesman; use strict; use warnings; 12 my $emp = new Salesman ( "Name", "Tom Savage", "Salary", 50000, # Call to constructor "Commission", 1500, ); $emp->set_Salary; # Call to the access method 13 $emp->display( "Name" , "Salary", "Commission"); # Call Salesman's display method (Output) 9 Stats for the Salesman ------------------------- Name: Tom Savage Salary: 51500 The class using this display method is Salesman Explanation
|