Почему подкласс не наследует константы своего родителя? - PullRequest
6 голосов
/ 19 октября 2011

Итак, я занимался своим бизнесом в Муси, и я подумал, что, может быть, было бы неплохо использовать константу в тех местах, где я использую числа, чтобы прояснить, что означают эти числа или в случае их изменения позже

Итак, в родительском классе я добавил стандартную «константу использования»

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;
}

, но дочерний класс не знает о константах, установленных в родительском!дох!

Я полагаю, мне нужно сделать магию лося, чтобы заставить это работать правильно, или что-то еще полностью.Мой поиск по этому вопросу не дал результатов = /

Ответы [ 5 ]

11 голосов
/ 19 октября 2011

Константы являются подпрограммами.

{
    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;

Имейте в виду, что константы - это подпрограммы с пустым прототипом. perl использует это, чтобы встроить их во время компиляции. Однако вызовы методов игнорируют прототипы, и поэтому наследуемые константы, доступ к которым осуществляется таким образом, не будут встроены.

6 голосов
/ 19 октября 2011

Это на самом деле упоминается в документации , только если мимоходом:

"Константы принадлежат пакету, в котором они определены. Чтобы сослаться на константу, определенную в другом пакете, укажите полное имя пакета, как в Some::Package::CONSTANT. Константы могут быть экспортированы модулями, а также могут быть вызваны как методы класса или экземпляра, то есть как Some::Package->CONSTANT или как $obj->CONSTANT, где $obj является экземпляром Some::Package. Подклассы могут определять свои собственные константы, чтобы переопределять их в их базовом классе. "

4 голосов
/ 20 октября 2011

Поскольку константы являются подпрограммами, и вы можете получить наследование, вызвав их, так как методы bit уже застрахованы до смерти, здесь есть другое вращение.

Если вы знаете, что работаете только с одним файлом, вы можете использовать лексические константы для объединения пакетов:

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 ...

Если вам не нужен perl, чтобы умереть при назначении константы, объявление our становится немного проще (и может быть my):

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

Вы можете вернуть постоянную природу, все еще используя краткий синтаксис с небольшим количеством волшебства:

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 ...

Вы, конечно, можете опустить $constant coderef и включить магию:

package Parent;
    Internals::SvREADONLY($_, 1)
        for our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2);
4 голосов
/ 19 октября 2011

Назовите их как метод.

sub printMyLevel{
    my ( $self, ) = $_;
    print $self->MY_LEVEL;
}
2 голосов
/ 19 октября 2011

Наследование влияет на вызовы методов ($x->m), точка.

...