How can I create an object in a parent class, but bless it in my child class in Perl?

I have two classes: the base class, Foo :: Base and the derived class, Foo::Base::Sub . I want Foo::Base::Sub do some type and data validation on a constructor argument - a hash - before blessing it. I tried to override the constructor Foo::Base->new by doing checks and then calling Foo::Base->new (since the code will be exactly the same):

 package Foo::Base::Sub; sub new { ...check argument type and data... Foo::Base->new(%my_hash) } 

The problem is that by invoking the Foo::Base construct, the hash will now be blessed as a Foo :: Base object, not a Foo :: Base :: Sub object. The obvious solution is to simply put the code from Foo::Base::new into Foo::Base::Sub::new , but then I repeat the code. Another thing is that Foo :: Base is not mine - so I would not want to change it after the module downloaded or deployed it unnecessarily.

It seems to me that this problem should have arisen earlier, and therefore there should be a canonical solution. Moreover, it does affect the type of enforcement, which is usually not a Perl issue.

So, is there a simple modification, or am I mistaken about this?

+4
source share
4 answers

The standard Perl idiom is to use SUPER to invoke the inheritance chain:

 @Foo::Base::Sub::ISA = qw(Foo::Base); sub new { my $package = shift; my $self = $package->SUPER::new(); # Other subconstructor stuff here return $self; } 

As noted in the comments, the Foo::Base constructor should use the two-factor bless form:

 sub new { my $package = shift; my $self = bless {}, $package; # Other superconstructor stuff here return $self; } 

When the superclass constructor is called, $package will be a subclass.

+8
source

I use to split into two parts: new and init .

 package Foo::Base; sub new { my $class = shift; my $self = bless {}, $class; return $self->init(@_); } sub init { my ($self, @params) = @_; # do something initialization and checks return $self; } package Foo::Sub; use base 'Foo::Base'; sub init { my ($self, @params) = @_; # do something initialization and checks $self = $self->SUPER::init(@params); # do something other if you wish return $self; } 

Note that 'Foo::Sub' does not implement the new constructor.

+7
source

You might want to take a look at the different ways to call super. The SUPER module may work, although I have not tried it myself.

0
source

While the β€œsuper” answers describe what you want here, you can (more generally) call the method method in arbitrary packages by doing something like this:

  $object = Foo::Class->new(...); $object->Bar::Class::method(@args); 

Bar :: Class :: method will act as usual, something like:

  package Bar::Class; sub method { my ($self, @args) = @_; 

only $ self will be of a different class than usual. Naturally, doing this rather than SUPER a lot is probably a sign that you are doing something wrong.

Compare also NEXT and Perl 5.10 'mro'.

-one
source

All Articles