You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 18:40:32