perlclass - Perl 类语法参考
use v5.38;
use feature 'class';
class My::Example 1.234 {
field $x;
ADJUST {
$x = "Hello, world";
}
method print_message {
say $x;
}
}
My::Example->new->print_message;
本文档描述了 Perl 的 class
特性的语法,该特性提供了支持面向对象编程范式的原生关键字。
从 Perl 5 开始,对对象的支持围绕着使用包名对引用进行“祝福”的概念。这样的引用可以用来调用它被祝福的包(或其任何父包)中的子例程。这个系统虽然简单,但足够灵活,可以允许创建多个更高级的、由社区驱动的面向对象系统。
类特性是类语法核心实现,与其他编程语言中的类语法类似。它不是一个 `bless` 包装器,而是一个完全内置于 Perl 解释器的新系统。
启用 `class` 特性允许在当前包的范围内使用以下新关键字
class NAME BLOCK
class NAME VERSION BLOCK
class NAME;
class NAME VERSION;
`class` 关键字声明一个新的包,该包旨在成为一个类。所有其他来自 `class` 特性的关键字都应该在这个声明的范围内使用。
class WithVersion 1.000 {
# class definition goes here
}
类可以在块或语句语法中声明。如果使用块,则块的主体包含类的实现。如果使用语句形式,则文件中的其余部分将被使用,直到下一个 `class` 或 `package` 语句。
`class` 和 `package` 声明类似,但类会自动获得一个名为 `new` 的构造函数 - 你不需要(也不应该)编写它。此外,在类块中,你可以声明字段和方法。
field VARIABLE_NAME;
field VARIABLE_NAME = EXPR;
field VARIABLE_NAME : ATTRIBUTES;
field VARIABLE_NAME : ATTRIBUTES = EXPR;
字段是在类范围内可见的变量 - 更具体地说,在 "method" 和 `ADJUST` 块中。每个类实例都有自己的字段存储,彼此独立。
字段的行为类似于普通的词法作用域变量。它有一个 sigil,并且对类私有(尽管创建访问器方法将使其从外部访问)。主要区别在于不同的实例在同一个作用域中访问不同的值。
class WithFields {
field $scalar = 42;
field @array = qw(this is just an array);
field %hash = (species => 'Martian', planet => 'Mars');
}
字段可以选择具有初始化表达式。如果存在,表达式将在每个对象实例的构造函数中进行评估。在每次评估期间,表达式可以使用任何先前设置的字段的值,以及查看作用域中的任何其他变量。
class WithACounter {
my $next_count = 1;
field $count = $next_count++;
}
当与 `:param` 字段属性结合使用时,默认表达式可以使用任何 `=`、`//=` 或 `||=` 运算符。使用 `=` 的表达式将在调用者根本没有向构造函数传递相应参数时应用。使用 `//=` 的表达式也将在调用者传递了参数但值为未定义时应用,而使用 `||=` 的表达式将在值为 false 时应用。
method METHOD_NAME SIGNATURE BLOCK
method METHOD_NAME BLOCK
method SIGNATURE BLOCK
method BLOCK
方法是旨在在类对象上下文中调用的子例程。
一个名为 `$self` 的变量将自动在 `method` 的词法作用域中创建,该变量填充了当前对象实例。
方法始终表现得好像use feature 'signatures'
生效一样,但就签名而言,$self
不会出现在参数列表中。
class WithMethods {
field $greetings;
ADJUST {
$greetings = "Hello";
}
method greet($name = "someone") {
say "$greetings, $name";
}
}
就像常规子程序一样,方法可以是匿名的
class AnonMethodFactory {
method get_anon_method {
return method {
return 'this is an anonymous method';
};
}
}
上面提到的关键字的具体方面是使用属性来管理的。所有属性都以冒号开头,并且可以在项目名称之后附加一个或多个属性,用空格分隔。
类可以通过使用:isa
类属性从一个超类继承。
class Example::Base { ... }
class Example::Subclass :isa(Example::Base) { ... }
继承的方法是可见的,可以被调用。字段始终是词法变量,因此不能通过继承访问。
:isa
属性可以请求基类的最小版本;它的应用类似于use
- 如果提供的版本过低,它将在编译时失败。
class Example::Subclass :isa(Example::Base 2.345) { ... }
如果:isa
属性尚未加载,它将尝试require
指定的模块。
具有:param
属性的标量字段将从传递给构造函数的命名参数中获取其值。默认情况下,参数将与字段具有相同的名称(减去其开头的$
符号),但可以在属性中指定不同的名称。
field $x :param;
field $y :param(the_y_value);
如果没有默认表达式,则构造函数需要该参数;调用者必须传递它,否则将抛出异常。使用默认表达式,它将变为可选。
还没有。
每个对象都以构造函数调用开始其生命周期。构造函数始终命名为new
,并且像对类名进行方法调用一样被调用
my $object = My::Class->new(%arguments);
在构造过程中,类字段将与%arguments
哈希进行比较,并在可能的情况下进行填充。
可以在构造过程中执行对象调整以运行用户定义的代码。这是借助ADJUST
块完成的,这些块按声明顺序调用。
它们类似于BEGIN
块,这些块在包编译期间运行。但是,它们还可以访问$self
词法变量(对象实例)和在此之前创建的所有对象字段。
在构建阶段完成后,对象就可以使用了。
使用 blessed
(Scalar::Util::blessed
或 builtin::blessed
)在对象上将返回类的名称,而 reftype
(Scalar::Util::reftype
或 builtin::reftype
)将返回字符串 'OBJECT'
。
就像其他引用一样,当对象引用计数达到零时,它将自动被销毁。
此功能仍处于实验阶段,并且非常不完整。以下列表概述了仍需添加或更改的工作类型。
角色
用于声明角色的语法(可能是 role
关键字),以及用于将角色消耗到类中的语法(可能是 :does()
属性)。
ADJUST 块的参数
用于声明 ADJUST
块可以消耗命名参数的语法,这些参数成为类构造函数 API 的一部分。这可能受到类似计划的启发,该计划将命名参数添加到子例程签名中。
class X {
ADJUST (:$alpha, :$beta = 123) {
...
}
}
my $obj = X->new(alpha => 456);
ADJUST 块作为真正的块
目前,每个 ADJUST 块都包装在自己的 CV 中,该 CV 使用完整的 ENTERSUB 开销进行调用。应该可以使用相同的机制,使所有字段初始化表达式出现在相同的 CV 中,以及 ADJUST 块,将它们合并到每个类的单个 CV 中。如果类有多个 ADJUST 块,这将使调用更快。
访问器生成器属性
用于请求为字段生成访问器方法的属性。可能是 :reader
和 :writer
。
class X {
field $name :reader;
}
等同于
class X {
field $name;
method name { return $name; }
}
元编程
元编程 API 的扩展(目前由 RFC0022 提出),它增加了对类、方法、字段、ADJUST 块和其他此类与类相关的细节的了解。
扩展定制
核心外模块与类系统交互的方式,包括它们提供新类或字段属性的能力。
Paul Evans
Bartosz Jarzyna