匿名类如何重写父类方法并传递构造参数?
解读
国内面试官问这道题,通常想验证三件事:
- 你是否真的用过 PHP7 之后推出的匿名类,而不是“只听过”;
- 能否把“继承”“构造传参”“方法重写”三件事一次性写对,且语法不踩坑;
- 是否知道匿名类一样遵循普通类的继承规则(构造器不会自动向上转发,必须手动 parent::__construct)。
现场写不出来,会被直接判定为“没有 PHP7+ 实际项目经验”,在简历筛选环节就可能被刷掉。
知识点
- 匿名类语法:new class(…) extends ParentClass { … }
- 构造参数传递:在 new class(实参) 的小括号里写,对应匿名类构造器的形参列表。
- 重写规则:方法名、可见性必须一致;想调用父类被覆盖的方法,用 parent::method()。
- 匿名类一样支持 use 语句引入外部变量,但面试题侧重“继承+构造”,use 不是得分点。
- 运行期特性:匿名类会在内部生成一个带随机后缀的类名,但开发者无感知,调试时可通过 get_class() 观察。
答案
<?php
// 父类
class Logger
{
protected string $channel;
public function __construct(string $channel)
{
$this->channel = $channel;
}
public function log(string $msg): void
{
echo "[{$this->channel}] {$msg}\n";
}
}
// 客户端代码:匿名类重写 log 并传递构造参数
$logger = new class('order') extends Logger
{
// 显式写出构造器,把参数继续传给父类
public function __construct(string $channel)
{
// 必须先调 parent,否则 $this->channel 未初始化
parent::__construct($channel);
}
// 重写父类方法
public function log(string $msg): void
{
// 先调父类,实现“装饰”效果
parent::log($msg);
echo '>>> 额外行为:记录时间 ' . date('Y-m-d H:i:s') . PHP_EOL;
}
};
// 运行验证
$logger->log('订单创建成功');
输出:
[order] 订单创建成功
额外行为:记录时间 2025-06-10 14:22:38
关键点:
- new class('order') 的小括号就是构造实参;
- 匿名类内部必须再写一次 __construct 形参列表,并手动 parent::__construct($channel);
- 重写方法时,签名必须与父类保持兼容(public 不能改成 protected,参数不能加类型限制之外的变更)。
拓展思考
- 如果父类构造器有 3 个以上参数,匿名类写法会显得冗长,实战里可用工厂方法封装:
static function createOrderLogger(): Logger { return new class(...) extends Logger {...}; } - 匿名类可以实现接口,也能同时继承父类;当接口与父类方法冲突时,优先满足接口的签名约束,这是面试的“陷阱”追问点。
- 在常驻进程(Swoole、WorkerMan)中,匿名类会被重复创建,导致 Opcache 生成多个匿名类缓存条目;高并发场景下建议改用普通具名类,避免内存碎片。
- 单元测试时,可用匿名类快速“打桩”外部依赖:
$stub = new class implements PaymentInterface { public function pay($amount) { return true; } };
这种写法在 TDD 代码评审中很常见,能体现你对语言特性的灵活运用。