Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't a subclass inherit its parent's constants?

So I was going about my Moosey business and I thought hey might be nice to use a constant in these places where I'm using numbers, to make it clear what these numbers mean or in case they change later

So in the parent class I added the standard 'use constant'

package Parent;
    use constant {
        NO_LEVEL => 0,
        MY_LEVEL => 1,
        YOUR_LEVEL => 2,
    };

package Child;
extends 'Parent';

#just to demonstrate that child can or cannot access the constant
sub printMyLevel{
 print MY_LEVEL;
}

but the child class is not aware of the constants set in the parent! doh!

I'm guessing I have to do some Moose magic to get this to work right, or something else entirely. My searching on this issue didnt pull up any results =/

like image 829
qodeninja Avatar asked Oct 19 '11 16:10

qodeninja


People also ask

Do subclasses inherit everything?

A subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses, but the constructor of the superclass can be invoked from the subclass.

Does a subclass inherit private variables?

Subclasses inherit public methods from the superclass that they extend, but they cannot access the private instance variables of the superclass directly and must use the public accessor and mutator methods.

Do subclasses inherit instance variables?

We can create subclasses of a given class that will inherit all of the instance variables and methods of the given class (called the super class) using the keyword extends . An example can be seen in the classes defined below.

Will all properties of a parent can acquire by child class in inheritance?

Inheritance concept is to inherit properties from one class to another but not vice versa. But since parent class reference variable points to sub class objects. So it is possible to access child class properties by parent class object if only the down casting is allowed or possible....


3 Answers

Constants are subroutines.

{
    package Parent;
    use Moose;
    use namespace::autoclean;

    use constant {
        NO_LEVEL => 0,
        MY_LEVEL => 1,
        YOUR_LEVEL => 2,
    };
    __PACKAGE__->meta->make_immutable;
};

{
    package Child;
    use Moose;
    use namespace::autoclean;

    extends 'Parent';

    sub printMyLevel {
        my $self = shift;
        my $class = ref $self;

        print $class->MY_LEVEL;
    }
    __PACKAGE__->meta->make_immutable;
}

package main;

my $child = Child->new;
$child->printMyLevel;

Keep in mind that constants are subroutines with an empty prototype. perl takes advantage of this to inline them during compilation. However, method calls disregard prototypes, and therefore inheritable constants accessed this way would not be inlined.

like image 100
Sinan Ünür Avatar answered Sep 21 '22 21:09

Sinan Ünür


This is actually mentioned in the documentation, if only in passing:

"Constants belong to the package they are defined in. To refer to a constant defined in another package, specify the full package name, as in Some::Package::CONSTANT. Constants may be exported by modules, and may also be called as either class or instance methods, that is, as Some::Package->CONSTANT or as $obj->CONSTANT where $obj is an instance of Some::Package. Subclasses may define their own constants to override those in their base class."

like image 31
Ilmari Karonen Avatar answered Sep 24 '22 21:09

Ilmari Karonen


Since the constants are subroutines and you can get inheritance by calling them as methods bit has been covered to death already, here is a different spin on things.

If you know you are only working in a single file, you can use lexical constants to bridge packages:

package Parent;
    our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL);
    *NO_LEVEL   = \0;  # this split declaration installs aliases to numbers
    *MY_LEVEL   = \1;  # into the lexicals.  since numbers are constants
    *YOUR_LEVEL = \2;  # to perl, the aliased names are also constants

package Child;

# just to demonstrate that anything below can access the constants
sub printAll {
    print "$NO_LEVEL $MY_LEVEL $YOUR_LEVEL\n";
}

Child->printAll; # 0 1 2

eval {$NO_LEVEL = 3} or print "error: $@\n";
# error: Modification of a read-only value attempted at ...

If you don't need perl to die when assigning to the constant, the our declaration gets a bit simpler (and could be a my):

our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2);

You can bring back the constant nature while still using the terse syntax with a little magic:

my $constant = sub {Internals::SvREADONLY($_[$_], 1) for 0 .. $#_};

package Parent;
    $constant->(our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2));

package Child;

# just to demonstrate that anything below can access the constants
sub printAll {
    print "$NO_LEVEL $MY_LEVEL $YOUR_LEVEL\n";  # interpolates :)
}

Child->printAll; # 0 1 2

eval {$NO_LEVEL = 3} or print "error: $@\n";
# error: Modification of a read-only value attempted at ...

You can of course omit the $constant coderef and inline the magic:

package Parent;
    Internals::SvREADONLY($_, 1)
        for our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2);
like image 32
Eric Strom Avatar answered Sep 22 '22 21:09

Eric Strom