确定是否已从基类

时间:2017-07-20 22:10:00

标签: perl inheritance

这个问题困扰了我,但我已经解决了问题并将发布我的答案。

我有一个名为Parent

的基类

package Parent;

sub new {
    my $c = shift;
    my $s = {}, bless $s, $c;
    return $s;
}

sub who {
    return "parent";
}

1;

这个父类暴露了一个方法; who

我写了两个子课程:ChildFreak。只有Child会覆盖who

package Child;

use base 'Parent';

sub who {
    return "child";
}

1;

畸形

package Freak;

use base 'Parent';

1;

基类如何确定是否已覆盖基本who方法?

我希望能够写出像这样的东西

package Parent;

sub new {
    my $c = shift;
    my $s = {}, bless $s, $c;
    return $s;
}

sub who {
    return "parent";
}

sub check {
  my $self = shift;

  my $is_overridden = 1; # what conditional should be here?

  return $is_overridden ? "yes" : "no";
}

1;

我可以使用什么条件来确定子程序是否被覆盖?

3 个答案:

答案 0 :(得分:7)

my $is_overridden = $self->can("who") != Parent->can("who");

请参阅UNIVERSAL包的文档,其中定义了can

另外,我应该添加一个更哲学的注释 - 听起来你的压倒性是违反了Liskov Subsitution Principle,重构事物可能会更好,以至于重写一个方法并不是你必须要做的事情。调查代码中的其他地方。

答案 1 :(得分:4)

Parent->UNIVERSAL::can('who') ne Child->UNIVERSAL::can('who')

UNIVERSAL::can(PACKAGE,METHODNAME)返回对给定方法名称在给定包中使用时将调用的子例程的引用。如果包不同但返回值相同,则意味着一个包从另一个包继承该方法(或者它们都是从公共源继承它)。此方法适用于多级继承:

package Parent;
sub foo { 42 }
sub bar { 19 }
sub baz { 47 }

package Child;
@Child::ISA = qw(Parent);
sub bar { 19 }

package Grandchild;
@Grandchild::ISA = qw(Child);
sub foo { 42 }

##############

package main;

print "foo:", Parent->UNIVERSAL::can('foo'), 
    Child->UNIVERSAL::can('foo'),
    Grandchild->UNIVERSAL::can('foo'),"\n";

print "bar:", Parent->UNIVERSAL::can('bar'), 
    Child->UNIVERSAL::can('bar'),
    Grandchild->UNIVERSAL::can('bar'),"\n";

print "baz:", Parent->UNIVERSAL::can('baz'), 
    Child->UNIVERSAL::can('baz'),
    Grandchild->UNIVERSAL::can('baz'),"\n";

典型输出:

foo:CODE(0x17e26e8)CODE(0x17e26e8)CODE(0x17e2b38)
bar:CODE(0x17e27a8)CODE(0x17e29b8)CODE(0x17e29b8)
baz:CODE(0x17e2850)CODE(0x17e2850)CODE(0x17e2850)

答案 2 :(得分:0)

我对这种困境的回答如下:

包中方法的存在可以通过静态检查其CODE引用来确定,如下所示:

if( defined &Child::who ){
  return "yes";
}

但是,由于父母可以被许多不同的孩子所覆盖,并且我不想手动引用所有这些孩子,我将获得对包裹的引用(这将是包裹以字符串形式命名并评估它。

sub check {
  my $self = shift;
  my $package = ref $self; # 'Freak' or 'Child', etc.

  my $is_overridden = eval "defined &".$package."::who";
  die "eval error occurred" if $@;

  return $is_overridden ? "yes" : "no";
}

你有它!