Recipe 13.11 Accessing Overridden Methods
13.11.1 Problem
Your class's constructor
method overrides the constructor of its parent class. You want your
constructor to invoke the parent class's constructor.
13.11.2 Solution
Learn about the special pseudoclass, SUPER.
sub meth {
my $self = shift;
$self->SUPER::meth( );
}
13.11.3 Discussion
In languages like C++ where constructors don't actually allocate
memory but just initialize the object, all base class constructors
are automatically invoked for you. In languages like Java and Perl,
you have to invoke them yourself.
To invoke a method in a particular class, the notation
$self->SUPER::meth( ) is used. This is an
extension of the regular notation that means to begin searching for a
method in a particular class. It is valid only from within an
overridden method. Here's a comparison of styles:
$self->meth( ); # Call wherever first meth is found
$self->Where::meth( ); # Start looking in package "Where"
$self->SUPER::meth( ); # Call overridden version
Simple users of the class should probably restrict themselves to the
first line in the previous example. The second is possible, but not
suggested for this situation, because we have the special notation
shown in the third line, which only works within the overridden
method.
An overriding constructor should invoke its SUPER's constructor to
allocate and bless the object, limiting itself to instantiating any
data fields needed. It makes sense here to separate the object
allocation code from the object initialization code for reasons that
will become clear a couple paragraphs from now. We'll name it with a
leading underscore, a convention indicating a nominally private
method. Think of it as a "Do Not Disturb" sign.
sub new {
my $classname = shift; # What class are we constructing?
my $self = $classname->SUPER::new(@_);
$self->_init(@_);
return $self; # And give it back
}
sub _init {
my $self = shift;
$self->{START} = time( ); # init data fields
$self->{AGE} = 0;
$self->{EXTRA} = { @_ }; # anything extra
}
Both SUPER::new and _init are
invoked with any remaining arguments. That way the user might pass
other field initializers in, as in:
$obj = Widget->new( haircolor => red, freckles => 121 );
Whether you store these user parameters in their own extra hash is up
to you.
Note that SUPER works only on the first
overridden method. If your @ISA array has several
classes, there could be several. A manual traversal of
@ISA is possible, but seldom worth the hassle.
my $self = bless { }, $class;
for my $class (@ISA) {
my $meth = $class . "::_init";
$self->$meth(@_) if $class->can("_init");
}
This fragile code assumes that all superclasses initialize their
objects with _init instead of initializing in the
constructor. It also assumes that a hash reference is used for the
underlying object.
For a slightly more general approach to accessing all overridden
methods, save the return value from the can( )
method, which is the code reference to the subroutine that would be
invoked through normal method invocation. Then use that reference for
indirect method invocation.
sub some_method {
my $self = shift;
my %seen;
print "some_method($self): checking all ancestors\n";
for my $parent (our @ISA) {
if (my $code = $parent->can("some_method")) {
$self->$code(@_) unless $seen{$code}++;
}
}
}
To avoid calling the same subroutine more than once, the
%seen hash keeps track of which subroutines have
been called. This could happen if several parent classes shared a
common ancestor.
Methods that would trigger an AUTOLOAD will not be
accurately reported unless that package has declared (but not
defined) the subroutines it wishes to have autoloaded.
13.11.4 See Also
The discussion on the SUPER class in perltoot(1)
and perlobj(1), and in the section on "Method
Invocation" in Chapter 12 of Programming
Perl
|