给出以下代码,看来实例化对象的顺序很重要。当我希望每个对象都有不同的列表时,下面的代码将为两个对象打印相同的列表,因为列表是在构建时创建的实例属性。
package t;
use Moo;
use Types::Standard qw(ArrayRef);
my @list = qw/foo bar baz/;
has list => (
is => 'rw',
isa => ArrayRef,
default => sub {\@list}
);
1;
---
package u;
use Moo;
use Types::Standard qw(ArrayRef);
extends 't';
sub BUILD {
my ($self) = @_;
push @{$self->list()}, qw/apple banana/;
return $self;
}
1;
---
#!perl
use Data::Printer;
use t;
use u;
my $u = u->new();
p $u->list();
my $t = t->new();
p $t->list();
当前输出:
\ [
[0] "foo",
[1] "bar",
[2] "baz",
[3] "apple",
[4] "banana"
]
\ [
[0] "foo",
[1] "bar",
[2] "baz",
[3] "apple",
[4] "banana"
]
预期输出:
\ [
[0] "foo",
[1] "bar",
[2] "baz",
[3] "apple",
[4] "banana"
]
\ [
[0] "foo",
[1] "bar",
[2] "baz"
]
答案 0 :(得分:7)
由于您要更改有问题的数组,因此您不希望引用用作默认\@list
的数组,因此希望获取浅表副本[@list]
。
package t;
use Moo;
use Types::Standard qw(ArrayRef);
my @list = qw/foo bar baz/;
has list => (
is => 'rw',
isa => ArrayRef,
builder =>
default => sub { [@list] }
);
package u;
use Moo;
use Types::Standard qw(ArrayRef);
extends 't';
sub BUILD {
my ($self) = @_;
push @{$self->list()}, qw/apple banana/;
return $self;
}
package main;
use Data::Printer;
my $u = u->new();
p $u->list();
my $t = t->new();
p $t->list();
尽管我可以使用BUILD修改属性,但不一定是最好的。您可以将类似lazy属性的内容与builder方法一起使用,然后在子类ala中重载该方法
package t;
use Moo;
use Types::Standard qw(ArrayRef);
my @list = qw/foo bar baz/;
has list => (
is => 'rw',
isa => ArrayRef,
builder => '_build_list',
lazy => 1,
);
sub _build_list {
my $self = shift;
return [@list];
}
package u;
use Moo;
extends 't';
sub _build_list {
my $self = shift;
my $list = $self->SUPER::_build_list();
push @$list, qw/apple banana/;
return $list;
}
package main;
use Data::Printer;
my $u = u->new();
p $u->list();
my $t = t->new();
p $t->list();