Perl packages, bless, and inheritance
Create objects as blessed hashes, override methods in a subclass, and call the parent version.
use strict;
use warnings;
package Animal;
sub new {
my ($class, %args) = @_;
my $self = { name => $args{name} };
return bless $self, $class;
}
sub name { return $_[0]{name} }
sub sound { return 'generic noise' }
sub speak {
my $self = shift;
return $self->name . ' says ' . $self->sound;
}
package Dog;
use parent -norequire, 'Animal';
sub sound { return 'Woof' }
sub speak {
my $self = shift;
return $self->SUPER::speak() . '!';
}
package main;
my @animals = (Animal->new(name => 'Generic'), Dog->new(name => 'Rex'));
print $_->speak, "\n" for @animals;
print $animals[1]->isa('Animal') ? "a Dog is an Animal\n" : "not an Animal\n";
print Dog->can('speak') ? "Dog can speak\n" : "Dog cannot speak\n";
print ref($animals[1]), "\n";Generic says generic noise Rex says Woof! a Dog is an Animal Dog can speak Dog
Explanation
Perl builds objects from ordinary data. A package is a namespace, and a class is a package whose subroutines are methods. bless attaches a package name to a reference, usually a hash, which makes it an object of that class. A method call $obj->method passes the object as the first argument, conventionally named $self.
Inheritance uses the parent pragma. -norequire tells it that the parent package is defined in the same file. A subclass can override a method and call the parent's version with SUPER::. isa tests whether an object belongs to a class or a subclass, can tests whether a method exists, and ref returns the class name. Method calls are resolved at run time, so the right version runs for each object.
Explore the dry run
use strict;use warnings; package Animal; sub new { my ($class, %args) = @_; my $self = { name => $args{name} }; return bless $self, $class;} sub name { return $_[0]{name} }sub sound { return 'generic noise' } sub speak { my $self = shift; return $self->name . ' says ' . $self->sound;} package Dog;use parent -norequire, 'Animal'; sub sound { return 'Woof' } sub speak { my $self = shift; return $self->SUPER::speak() . '!';} package main; my @animals = (Animal->new(name => 'Generic'), Dog->new(name => 'Rex'));print $_->speak, "\n" for @animals;print $animals[1]->isa('Animal') ? "a Dog is an Animal\n" : "not an Animal\n";print Dog->can('speak') ? "Dog can speak\n" : "Dog cannot speak\n";print ref($animals[1]), "\n";Follow the example
Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.
View the complete dry-run table
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| bless turns the plain hash into an object of the class that new was called on. | — | — | |
| Dog inherits from Animal. -norequire says the parent is in this file. | — | — | |
| Call the parent's speak and add an exclamation mark. | — | — | |
| The class name is passed to new as $class, so Dog->new creates a Dog. | — | — | |
| Each object runs its own class's speak. Animal::speak calls sound, which resolves to Dog::sound for the dog. | — | Generic says generic noise Rex says Woof! | |
| isa follows the inheritance chain. | — | Generic says generic noise Rex says Woof! a Dog is an Animal | |
| can checks that a method exists, including inherited ones. | — | Generic says generic noise Rex says Woof! a Dog is an Animal Dog can speak | |
| ref on a blessed reference returns the class name. | — | Generic says generic noise Rex says Woof! a Dog is an Animal Dog can speak Dog |
Cost and common mistakes
Method dispatch is O(depth of the inheritance chain) per call, with caching.
- Object data in a plain hash is public by convention only. Modules such as Moo, Moose, and Object::Pad add stricter classes.
- Forgetting use strict in each file hides typos in method and variable names.
- If a subclass overrides new, it must call the parent's new through SUPER::new, or the parent's fields are not set.
Try it yourself
How would you add a Cat class?
Reveal answer and reasoning
Add package Cat; use parent -norequire, 'Animal'; sub sound { return 'Meow' }. The existing speak method works unchanged.