Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't $class->SUPER::new call the constructors of all parent classes when using multiple inheritance?

I am trying to use multiple inheritance in Perl, but I can't figure out how to call multiple parent constructors from the child constructor.

A.pm:

package A;
use Carp qw (croak);
use strict;
use warnings;

sub new {
    my $class = shift;
    print "This is A new\n";
    my $self->{DEV_TYPE} = shift || "A";
    bless($self, $class);
    return $self;
}

sub a_func{
    print "This is A func\n";
}

1;

B.pm:

package B;
use Carp qw (croak);
use strict;
use warnings;

sub new {
    my $class = shift;
    print "This is B new\n";
    my $self->{DEV_TYPE} = shift || "B";
    bless($self, $class);
    return $self;
}

sub b_func{
    print "This is B func\n";
}

1;

C.pm:

package C;
use Carp qw (croak);
use strict;
use warnings;
eval "use A";
die $@ if $@;
eval "use B";
die $@ if $@;
our @ISA = ("A","B");

sub new {
    my $class = shift;
    my $self = $class->SUPER::new(@_);
    print "This is C new\n";
    $self->{DEV_TYPE} = shift || "C";
    bless($self, $class);
    return $self;
}

sub c_func{
    print "This is C func\n";
}

1;

In C::new, $class->SUPER::new doesn't call the constructor for B. If I call it explicitly with $class->B::new(@_);, I get the error

Can't locate object method "new" via package "B" at C.pm

What am I doing wrong?

like image 915
Omer Levy Avatar asked Mar 10 '23 14:03

Omer Levy


1 Answers

$class->SUPER::new always calls A::new because A comes before B in @ISA. See method resolution order in perlobj:

When a class has multiple parents, the method lookup order becomes more complicated.

By default, Perl does a depth-first left-to-right search for a method. That means it starts with the first parent in the @ISA array, and then searches all of its parents, grandparents, etc. If it fails to find the method, it then goes to the next parent in the original class's @ISA array and searches from there.

This means that $class->SUPER::new will only call one of the parent constructors. If you have initialization logic in both parent classes that needs to be run from the child, move it into separate methods as described in this post.


When you explicitly call B::new with $class->B::new, you get

Can't locate object method "new" via package "B" at C.pm

because use B is loading the core module B instead of your module. You should rename your module.


Note that it's better to use the parent pragma instead of setting @ISA manually, e.g

use parent qw(Parent1 Parent2);

parent takes care of loading the parent modules, so you can drop the associated use statements (which you shouldn't be evaling, by the way).

like image 175
ThisSuitIsBlackNot Avatar answered Mar 15 '23 23:03

ThisSuitIsBlackNot