You already know that inheritance means that properties and methods of a parent class will be available to child classes. This section shows you can use inheritance in Perl.
First, a little diversion. You may not have realized it yet, but each package can have its own set of variables that won't interfere with another package's set. So if the variable $first was defined in package A, you could also define $first in package B without a conflict arising. For example inher.pl,
package A; $first = "package A"; package B; $first = "package B"; package main; print("$A::first\n"); print("$B::first\n");
displays
package A package B
Notice that the :: is being used as a scope resolution operator in this example. The -> notation will not work; also, it's okay that -> can't be used because we're not really dealing with objects in this example, just different namespaces.
You're probably wondering what this diversion has to do with inheritance, right? Well, inheritance is accomplished by placing the names of parent classes into a special array called @ISA. The elements of @ISA are searched left to right for any missing methods. In addition, the UNIVERSAL class is invisibly tacked on to the end of the search list. For example universal.pl,
package UNIVERSAL; sub AUTOLOAD { die("[Error: Missing Function] $AUTOLOAD @_\n"); } package A; sub foo { print("Inside A::foo\n"); } package B; @ISA = (A); package main; B->foo(); B->bar();
displays
Inside A::foo [Error: Missing Function] B::bar B
Let's start with the nearly empty class B. This class has no properties or methods; it just has a parent: the A class. When Perl executes B->foo(), the first line in the main package, it first looks in B. When the foo() function is not found, it looks to the @ISA array. The first element in the array is A, so Perl looks at the A class. Because A does have a foo() method, that method is executed.
When a method can't be found by looking at each element of the @ISA array, the UNIVERSAL class is checked. The second line of the main package, B->bar(), tries to use a function that is not defined in either the base class B or the parent class A. Therefore, as a last-ditch effort, Perl looks in the UNIVERSAL class. The bar() function is not there, but a special function called AUTOLOAD() is.
The AUTOLOAD() function is normally used to automatically load undefined functions. Its normal use is a little beyond the scope of this book. However, in this example, I have changed it into an error reporting tool. Instead of loading undefined functions, it now causes the script to end (via the die() function) and displays an error message indicating which method is undefined and which class Perl was looking in. Notice that the message ends with a newline to prevent Perl from printing the script name and line number where the script death took place. In this case, the information would be meaningless because the line number would be inside the AUTOLOAD() function.
The next listing below shows how to call the constructor of the parent class. This example shows how to explicitly call the parent's constructor. In the next section, you learn how to use the @ISA array to generically call methods in the parent classes. However, because constructors are frequently used to initialize properties, I feel that they should always be called explicitly, which causes less confusion when calling constructors from more than one parent.
This example also shows how to inherit the properties of a parent class. By calling the parent class constructor function, you can initialize an anonymous hash that can be used by the base class for adding additional properties.
It operates as follows:
The Perl code to perform the above is invent2.pl:
#!/usr/bin/perl use strict; use warnings; package Inventory_item; sub new{ my $class = shift; my %params = @_; bless { "PART_NUM" => $params{"PART_NUM"}, "QTY_ON_HAND" => $params{"QTY_ON_HAND"}, # Considered best practice to keep the superfluous comma }, $class; } package Pen; #@ISA = (Inventory_item); sub new { my $class = shift; my %params = @_; my $self = Inventory_item->new(@_); $self->{"INK_COLOR"} = $params{"INK_COLOR"}; return bless($self, $class); } #package main; my $pen = Pen->new( "PART_NUM" => "12A-34", "QTY_ON_HAND" => 34, "INK_COLOR" => "blue", ); print "The part number is " . $pen->{'PART_NUM'} . "\n"; print "The quantity is " . $pen->{'QTY_ON_HAND'} . "\n"; print "The ink color is " . $pen->{'INK_COLOR'} . "\n"; # added different printing options. More educational I think printf "The part number is %s\n",$pen->{'PART_NUM'}; printf "The quantity is %s\n", $pen->{'QTY_ON_HAND'}; printf "The ink color is %s\n", $pen->{'INK_COLOR'}; print <{'PART_NUM'} The quantity is $pen->{'QTY_ON_HAND'} The ink color is $pen->{'INK_COLOR'} END
This program displays:
The part number is 12A-34 The quantity is 34 The ink color is blue
You should be familiar with all the aspects of this script by now. The line my($self) = Inventory_item->new(@_); is used to get a reference to an anonymous hash. This hash becomes the object for the base class.
To understand that calling the parent constructor creates the object that becomes the object for the base class, you must remember that an object is the anonymous hash. Because the parent constructor creates the anonymous hash, the base class needs a reference only to that hash in order to add its own properties. This reference is stored in the $self variable.
You may also see the variable name $this used to hold the reference in some scripts. Both $self and $this are acceptable in the object-oriented world.