为new方法参数设默认值导致对象构造行为异常的原因咨询
Raku自定义multi new方法的行为异常解析
初始代码的正常行为
初始代码定义了一个带位置参数的自定义new方法:
class Box { has $.data; multi method new($d) { say 'here'; self.bless(data => $d); } } my $box = Box.new('hi'); say $box.data; my $box2 = Box.new(data => 'be'); say $box2.data;
输出符合预期:
here hi be
原因是:
- 调用
Box.new('hi')时,匹配自定义的new($d)候选,执行say 'here'并将$.data设为'hi' - 调用
Box.new(data => 'be')时,因为没有传递位置参数,无法匹配自定义的new($d)(该候选必须接收一个位置参数),因此自动匹配类默认生成的new方法——默认new接受命名参数直接初始化属性,所以$.data被正确设为'be',且不会执行自定义new里的输出语句。
添加默认值后的异常行为
当给$d添加默认值后:
class Box { has $.data; multi method new($d = '') { # 为$d设置默认值 say 'here'; self.bless(data => $d); } } my $box = Box.new('hi'); say $box.data; my $box2 = Box.new(data => 'be'); say $box2.data;
输出变为:
here hi here
(注:最后一行say $box2.data输出为空字符串)
异常原因如下:
- 自定义
new($d = '')变成了可接收0个或1个位置参数的候选 - 调用
Box.new(data => 'be')时,没有传递位置参数,满足自定义候选的位置参数要求($d使用默认值'') - Raku中所有方法默认允许传递额外的命名参数(即使签名未声明),因此
data => 'be'会被当作额外参数传入,但自定义方法完全未处理该命名参数,仅将$d的默认值''传给self.bless,导致$.data被设为空 - 同时自定义
new里的say 'here'被执行,因此多输出了一行here
添加*%_ ()后的修复原理
将签名改为multi method new($d = '', *%_ ())后恢复正常,原因是:
*%_ ()是空的 slurpy 命名参数捕获,显式声明该方法可接受任意命名参数但不绑定变量- Raku的多方法匹配会优先选择更精确的候选:类默认的
new方法签名是multi method new(*%attrs),专门匹配仅传递命名参数的调用;而自定义的new($d = '', *%_ ())需要满足“位置参数(可默认)+ 任意命名参数”的组合。此时调用Box.new(data => 'be')时,默认new是更精确的匹配,因此被选中,回到初始代码的正常行为。
内容的提问来源于stack exchange,提问作者StevieD
相关产品推荐
相关产品推荐

