代码正常运行:
use v6.d;
class Foo {
has $.name;
submethod BUILD (:$!name = 'John') {};
}
my $f = Foo.new;
say $f;
# OUTPUT: Foo.new(name => "John")
然而,当我添加:
class Bar is Foo {
submethod BUILD (:$!name = 'Jane') {};
}
my $b = Bar.new;
say $b;
我得到这个错误信息:
= = =对不起!===编译时出错。属性
$!name
未在类Bar
atscratch.raku:14
中声明
如何在构造时分配默认值?
以下是根据您的评论编辑的版本。这样,子类的默认值在子类中设置,但如果需要,可以显式设置。
use v6.d;
class Foo {
has $.name = 'John'
}
class Bar is Foo {
method new( *%h ) {
%h<name> //= 'Jane';
nextwith( |%h )
}
}
Foo.new.say; # OUTPUT: Foo.new(name => "John")
Bar.new.say; # OUTPUT: Bar.new(name => "Jane")
Bar.new(name => 'Dora').say; # OUTPUT: Bar.new(name => "Dora")
由于我之前的版本依赖于TWEAK,我想尝试一下也会很有趣。
class Foo {
has $!name;
multi method name { #getter
$!name
}
multi method name( $value ) { #setter
$!name = $value
}
submethod TWEAK {
$!name //= 'John'
}
method gist(::T:) { #captures type of its invocant
"{::T.^name}.new(name => "$!name")"
}
}
class Bar is Foo {
submethod TWEAK( :$name ) {
self.name: $name // 'Jane'
}
}
Foo.new.say; # OUTPUT: Foo.new(name => "John")
Bar.new.say; # OUTPUT: Bar.new(name => "Jane")
Bar.new(name => 'Dora').say; # OUTPUT: Bar.new(name => "Dora")
这有点棘手,因为公共属性简写has $.name;
自动生成(i)公共访问器getter/setter方法和(ii)代理,以便于分配和(iii)调整.gist
,以便通过.say
快速方便地查看所有公共属性。但是这些功能在施工完成之前还没有准备好。
因此,这个例子必须有显式的setter/getter多方法,has $!name;
作为私有属性。
公共属性简写就像OO的训练轮,它提供了简单的基本OO使用,作为透明的数据结构,具有较低的学习曲线(如Python)。回避这一点就像将raku放入更正式的OO模式并进行适当的封装。随着继承、角色、委托、信任等的不断深入,raku逐渐鼓励您变得更加正式。
这里我们需要这样做,以便在构造期间访问父类中的$!name
属性。
对于Foo的TWEAK子方法来说,现在很容易通过分配给它的私有$!name来应用默认值。
现在Bar的TWEAK子方法可以使用getter/setter,因为所有常规方法在TWEAK时都可用。(实际上,如果在本例中使用BUILD而不是TWEAK,则会得到相同的结果。)
其他一些调整-
- say在对象上调用
.gist
-所以如果你想显示它们,我们需要显式地将私有属性放回自定义.gist
中 - 这里我使用
::T
类型捕获,所以我只需要做一次并且相同的父方法适用于所有子类 - 需要在调整时使用
self.
而不是$.
(所有$.
虚拟方法机制随后进入),和 - 需要使用setter方法
self.name: 'value'
的方法语法(IMO是self.name('value')
更好的方式),因为公共attr代理不存在
在编辑之后,下面的内容有点脱离上下文,但我还是要离开这里。
神奇之处在于特殊的方法new, BUILD和TWEAK将根据它们的签名自动设置命名属性。
这就是为什么我更喜欢使用TWEAK而不是BUILD:
BUILD可以设置属性,但是它没有访问属性的权限声明为其默认属性的内容应用。另一方面,在默认值之后调用TWEAK已应用,因此将发现属性已初始化。所以它可以用来检查事物或修改对象后的属性吗建设。
这里是一个使用TWEAK
的方法,改编自brian d foy的书,"Learning Raku"(原"Learning perl "),第12章:"Classes":
use v6.d;
class Foo {
has $!default-name = 'John';
has $.custom-name is rw;
submethod TWEAK (:$custom-name) {
self.custom-name = $custom-name // $!default-name;
};
}
my $f = Foo.new;
say $f;
put "This is class Foo with {$f.custom-name} as name.";
class Bar is Foo {}
my $b = Bar.new;
$b.custom-name = 'Jane';
say $b;
put "This is class Bar with {$b.custom-name} as name.";
输出:
Foo.new(custom-name => "John")
This is class Foo with John as name.
Bar.new(custom-name => "Jane")
This is class Bar with Jane as name.
类Foo的"John"当没有分配自定义名称时,使用默认名称。在第二个例子中Bar's "Jane"接受分配给它的自定义名称。
您可以修改class Bar
,使其拥有自己的default-name
和TWEAK
子方法。那么,原始class Foo
和继承的class Bar is Foo {}
之间的唯一区别似乎是将$.default-name
声明为公共方法。
https://www.learningraku.com/2018/08/14/table-of-contents/
https://www.oreilly.com/library/view/learning-perl-6/9781491977675/
我认为这接近你想要做的,尽管它需要使字段名可变,这里声明为"is rw":
class Foo {
has $.name is rw = 'John'; # easy way to specify a default
}
class Bar is Foo {
submethod TWEAK (:$name) {
self.name = $name // 'Jane';
};
}
my $f = Foo.new;
say $f; # Foo.new(name => "John")
my $b = Bar.new;
say $b; # Bar.new(name => "Jane")
这会给你一个带有不同默认值的子类,虽然你可能认为这是你可以改变的缺点对象创建后的名称:
$b.name = 'Janezilla';
say $b; # Bar.new(name => "Janezilla")
还有另一种选择——不需要在Foo中创建属性——是使用组合,而不是将继承与handles特性结合使用。
use v6.d;
class Foo {
has $.name = 'John' ;
}
class Bar {
has Foo $!foo handles 'name' ;
submethod TWEAK( :$name = 'Jane' ) {
$!foo .= new: :$name
}
}
say Foo.new.name ; # "John"
say Bar.new.name ; # "Jane"
say Bar.new(name => "Terrie").name # "Terrie"
可以说,handles子句更清楚地声明了你的意图——将Foo中的'name'属性作为你自己的。有人会说,在任何情况下,复合都是比继承更好的方法——但显然,这将取决于你在更广泛的上下文中做这件事。