Item 50: Understand method inheritance in Perl.


Inheritance, like many other things in Perl, is pared down to its bare essentials. Perl has direct support for method inheritance only. It does not provide a built-in mechanism for inheriting datasee Item 51.

Method inheritance

To derive class B from class A , you add the name 'A' to class B 's @ISA variable. For example:

 package B;  @ISA = qw(A); 

For each class, the package variable @ISA defines a list of parent classes that supply inherited methods. Inherited methods are invoked when Perl cannot find a method in the first place it looks. Remember that method calls like the following:

 $foo = Method Class('arg1', 'arg2'); 

are translated into:

 $foo = Class::Method('Class', 'arg1', 'arg2'); 

What if there is no Class::Method ? If @ISA is empty, then the result is a run-time fatal error. However, if @ISA contains one or more class names , then Perl also looks in those classes for a subroutine named Method and, if necessary, to the parent classes of those classes, and so on, yielding a fatal error only after it has exhausted its search of the inheritance tree.

Let's see how this actually works. Here's a class hierarchy:

 package A;  sub new { bless {}, shift };  sub A_Method {    print "A_Method\n";  } 

Define a top-level class A and a bare-bones "all-purpose" constructor.

 package B;  @ISA = qw(A);  sub B_Method {    print "B_Method\n";  }  sub Method {    print "Method (in B)\n";  } 

Define a class B derived from A .

 package C;  @ISA = qw(B);  sub C_Method {    print "C_Method\n";  }  sub Method {    print "Method (in C)\n";  } 

Define a class C derived from B .

It also derives indirectly from A .

And here are some examples using the classes A , B , and C :

 package main; 

All our examples are in main .

 $a = new A; 

Create a new object of class A .

 $b = new B; 

B has no method new , so this calls A::new('B') and creates a new object blessed into class B .

 $c = new C; 

Looks for new in B , then in A , calls A::new('C') and creates a new object blessed into class C .

 $c->C_Method(); 

Prints C_Method .

 $c->Method(); 

Prints Method (in C) .

 $c->B_Method(); 

Prints B_Method .

 $c->A_Method(); 

Prints A_Method .

 $b->Method(); 

Prints Method (in B) .

 $a->Method(); 

Run-time fatal error.

Inheritance in Perl is a run-time mechanism. It is possible to modify @ISA at run time and thereby change the inheritance hierarchy of a program "on the fly." In most cases this would be a bad or at least a very strange idea; however, it could be useful for implementing some kind of template or dynamic loading mechanism.

Multiple inheritance

Multiple inheritance is Considered Scary [3] in many programming languages, but in Perl it is a simple and widely used feature. The absence of data inheritance makes multiple inheritance much less confusing.

[3] A feeble attempt at paraphrasing the title of Edsger W. Dijkstra's famous "Goto Statement Considered Harmful" letter from the March 1968 Communications of the ACM . Discussions of multiple inheritance sometimes raise the same kind of ideological arguments.

To inherit methods directly from more than one class, you add more than one class name to @ISA :

 package A1;  sub A1_Method {    print "A1_Method\n";  }  sub Method {    print "Method (in A1)\n";  } 

Define a top-level class A1 .

 package A2;  sub A2_Method {    print "A2_Method\n";  }  sub Method {    print "Method (in A2)\n";  } 

Define a top-level class A2 .

 package B;  @ISA = qw(A1 A2);  sub new { bless {} } 

Define a class B that derives from both A1 and A2 .

Perl searches parent classes in the order in which they are encountered in @ISA . This is a depth-first search, meaning that Perl searches the first class listed in @ISA as well as all of its parent classes before searching the second class in @ISA .

Here are some examples using the classes A1 , A2 , and B :

 package main; 

All our examples are in main .

 $b = new B; 

Creates a new object of class B .

 $b->A1_Method(); 

Prints A1_Method .

 $b->A2_Method(); 

Prints A2_Method .

 $b->Method(); 

Prints Method (in A1) A1 is first in @B::ISA .

 @B::ISA = qw(A2 A1);  $b->Method(); 

Search A2 first.

Now prints Method (in A2) .

 A1::Method('A1'); 

Invoke A1::Method explicitly note class name argument to conform to method calling conventions.

If you don't want to search the entire inheritance hierarchy for a method, you can qualify the method name with the name of the package where you want the search to start:

Continued from above:

 $b->A1::Method(); 

Search A1 and its parent classes (if any).

Prints Method (in A1) .

 $b->A2::Method(); 

Prints Method (in A2) .

The special pseudo package name SUPER:: can be used to refer to the @ISA list for the current package :

Continued from above:

 package B; 

SUPER:: refers to the current package, not the class of the object to the left of the arrow.

 $b->SUPER::Method(); 

Search @B::ISA for Method , skipping B itself.

The most commonly encountered use of multiple inheritance is in creating modules (see Item 45). The top-level package for most Perl modules is generally a subclass of Exporter and is often also a subclass of AutoLoader or SelfLoader .

More about method inheritance AUTOLOAD and UNIVERSAL

After searching @ISA for methods, Perl also tries two other special locations: an AUTOLOAD subroutine in the search path and the UNIVERSAL class.

Perl calls a package's AUTOLOAD subroutine, if one exists, whenever an attempt is made to call a nonexistent subroutine in that package. Perl will also look in all the packages in the class hierarchy for an AUTOLOAD subroutine if the original subroutine's package lists one or more parent classes in @ISA and if the subroutine was invoked using method call syntax. [4]

[4] In earlier versions of Perl 5, the inheritance tree was searched for AUTOLOAD subroutines whether or not a nonexistent subroutine was called using method call syntax. When brought to the attention of the Perl maintainers, this behavior was ruled a bug. In some future release it will be changed so that the inheritance tree is searched for AUTOLOAD subroutines only in the case of method calls and not ordinary subroutine calls.

An AUTOLOAD subroutine is easy to write. The variable $AUTOLOAD contains the fully qualified name of the subroutine called, and the arguments to the subroutine are the ones from the original subroutine call:

 package A;  sub new { bless {}, shift }  sub AUTOLOAD {    print "auto $AUTOLOAD\n";    print "@_\n";  } 

Define a parent class A with an AUTOLOAD subroutine.

 package B;  @ISA = qw(A); 

Define an empty class B that inherits from A .

 package main;  $b = new B;  $b->foo('bar', 'baz'); 

Prints:

auto B::foo

B=HASH(0x116098) bar baz

If you try this code out yourself you will see something interesting. In addition to autoloading B::foo , this example also autoloads B:: DESTROY! This may seem surprising at first, but remember that blessed objects like $b are automatically destroyed when they go out of scope or when the program terminates. When Perl attempts to invoke the destructor for $b , it uses the same search path as for any other method, and thus it autoloads the destructor.

Finally, if Perl cannot conclude its search for a missing method or subroutine via AUTOLOAD , it checks the " package of last resort"the UNIVERSAL package. You could even combine UNIVERSAL and AUTOLOAD to write a truly universal subroutine:

 package UNIVERSAL;  sub AUTOLOAD {    print "auto $AUTOLOAD\n";  } 

Any unresolved subroutine or method call will wind up here.

Avoid calling methods as subroutines

Method call syntax isn't just a fancy way to refer to a subroutine in another package. Method calls support inheritance, which means that super-classes are searched when a nonexistent method is called. You can't get the equivalent behavior with an ordinary subroutine call:

 @Student::ISA = qw(Person);  $aStudent->drive($aCar); 

Here's a method call.

 Student::drive($aStudent, $aCar); 

An equivalent call if drive is in Student .

 Person::drive($aStudent, $aCar); 

But this is the equivalent if drive is not found in Student , but in its parent class, Person .

If you call the subroutine Student::drive subroutine directly, and it doesn't exist, you'll get an error. The method call would have searched both Student and Person , finding it in Person . And you especially don't want to call a method directly without passing the initial class or instance parameter, because that would very likely confuse the method.



Effective Perl Programming. Writing Better Programs with Perl
Effective Perl Programming: Writing Better Programs with Perl
ISBN: 0201419750
EAN: 2147483647
Year: 1996
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net