Build a Small Perl Class with Constructors and Inheritance
You will finish with a runnable Perl object model: a class with a constructor and accessors, a subclass that overrides a method, and a short test that shows method dispatch. The example uses Perl's built-in OO features, so it needs no CPAN module and changes no system files.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about twenty minutes. You need Perl 5 and a shell. This guide was checked against Perl 5.38.2 and the installed perl-doc tutorial dated 14 September 2026. The tutorial itself says its last major revision was in 2013, so treat its discussion of CPAN OO systems as a starting point and check a project's current documentation before adopting it.
Checkpoint: everything below runs as an ordinary user. There is no reason to use sudo. The commands create only a temporary example if you choose to save one; the code does not open, edit or delete any real data.
1. Confirm the Perl version
Start by checking the interpreter that will run the example. This is read-only:
$ perl -v
This is perl 5, version 38, subversion 2 (v5.38.2) built for x86_64-linux-gnu-thread-multi
Your build string can differ. The useful part is the major, minor and patch version. Perl's built-in OO model is deliberately small: a package can act as a class, a reference can become an object when it is blessed, and a method is an ordinary subroutine called through the arrow operator.
2. Create a class with a constructor
Save this as perl-oo-demo.pl in a scratch directory, or paste it into an existing test file. The new method receives the class name as its first argument when called as Note->new. It returns a hash reference blessed into that class.
use strict;
use warnings;
package Note;
sub new {
my ($class, %args) = @_;
die "title is required\n" unless exists $args{title};
return bless {
title => $args{title},
text => $args{text} // '',
}, $class;
}
sub title {
return $_[0]{title};
}
sub text {
my ($self, $new_text) = @_;
$self->{text} = $new_text if @_ > 1;
return $self->{text};
}
sub summary {
my ($self) = @_;
return $self->title . ': ' . $self->text;
}
package main;
my $note = Note->new(
title => 'Release checklist',
text => 'Run the tests before tagging.',
);
print $note->summary, "\n";
$note->text('Run the tests, then tag the release.');
print $note->summary, "\n";
Run a syntax check before executing it:
$ perl -c perl-oo-demo.pl
perl-oo-demo.pl syntax OK
$ perl perl-oo-demo.pl
Release checklist: Run the tests before tagging.
Release checklist: Run the tests, then tag the release.
The hash keys are the object's attributes here. The title method is read-only because it has no setter; text is read-write because a second argument changes it. Accessors give callers a stable interface while leaving the internal representation changeable later.
3. Understand the arrow and the invocant
In $note->summary, Perl passes $note as the first argument. That first argument is the invocant: it can be an object or a class name. A method is not a separate language construct, so these are all ordinary subroutines in the Note package.
The constructor call passes Note as the invocant. That is why new stores the value in $class and blesses the new reference into it. Calling bless does not validate the fields, make them private or create accessors. Those are responsibilities of the class.
Checkpoint: deliberately exercise the constructor's validation without changing the file:
$ perl -I. -Mlib=. -e 'require "./perl-oo-demo.pl"; Note->new(text => "missing title");'
title is required
The command also prints the demo's normal output before the error because loading this simple file runs its package main section. In a real module, keep demonstrations and tests outside the module file so loading it has no side effects.
4. Add a subclass and override a method
Append this package before the existing package main section, then replace the main section with the version below. The @ISA declaration makes ChecklistNote inherit from Note. In separate module files, the tutorial's use parent 'Note' is a clearer way to declare the same relationship.
package ChecklistNote;
our @ISA = ('Note');
sub summary {
my ($self) = @_;
return '[check] ' . $self->SUPER::summary;
}
package main;
my $note = Note->new(
title => 'Release checklist',
text => 'Run the tests before tagging.',
);
my $check = ChecklistNote->new(
title => 'Security checklist',
text => 'Review permissions before deployment.',
);
print $note->summary, "\n";
print $check->summary, "\n";
Run the revised file:
$ perl -c perl-oo-demo.pl
perl-oo-demo.pl syntax OK
$ perl perl-oo-demo.pl
Release checklist: Run the tests before tagging.
[check] Security checklist: Review permissions before deployment.
ChecklistNote inherits new, title and text, but its own summary wins when the object is a checklist. SUPER::summary calls the parent implementation. This is method resolution and overriding in a form you can inspect directly.
5. Check the object's class
A blessed reference carries its class name. Use the core Scalar::Util module when you need to inspect that fact, rather than relying on the object's hash layout:
$ perl -I. -e 'require "./perl-oo-demo.pl"; require Scalar::Util; my $n = Note->new(title => "x"); print Scalar::Util::blessed($n), "\n";'
Note
This tells you what the reference was blessed into. It does not prove that the object contains every expected attribute or that a method will succeed. Constructor checks and method contracts still need tests.
6. Choose an OO system for a real project
The built-in model is useful for understanding old code and for very small examples, but the local perlootut(1) guide recommends using a CPAN OO system rather than repeating boilerplate in production classes. Its options have different costs:
Moosesupplies declarative attributes, constructors, roles, type checks and introspection, at the cost of a larger dependency and slower module loading.Moooffers a smaller feature set and faster loading, with an API designed to interoperate with Moose.Class::Tinyis a minimal, dependency-free choice with read-write accessors.Class::Accessorsits between hand-written classes and larger systems, and can use a Moose-like attribute syntax.
Roles model a capability that a class does, such as being switchable, while inheritance models a more specific kind of thing. If two unrelated classes need the same API, prefer a role where the chosen OO system supports one. Avoid multiple inheritance unless you have a specific, tested reason: method resolution becomes harder to follow.
Do not install one of these modules merely to run this guide. Check the module's current documentation, supported Perl versions and dependency policy before adding it to a project.
Done means
perl -c perl-oo-demo.plreportssyntax OK.- The constructor returns a blessed object and rejects a missing title.
- Accessors expose attributes without requiring callers to know the hash layout.
- A subclass inherits methods, overrides
summaryand calls the parent withSUPER. - You can explain when a role is a better fit than an inheritance hierarchy.
- No elevated command or persistent system change was needed.