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
區塊內。每個類別實例都會取得它們自己的欄位儲存,彼此獨立。
欄位表現得像一個正常的詞彙範圍變數。它有一個符號,而且是類別的私有 (儘管建立一個存取器方法會讓它從外部可存取)。主要差異在於不同的實例會在同一個範圍內存取不同的值。
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
hash 進行比較,並在可能的情況下填入資料。
物件調整可以在建構期間執行,以執行使用者定義的程式碼。這是透過 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 負載呼叫。應該可以使用相同的機制,讓所有欄位初始化器表達式都出現在 ADJUST 區塊中的同一個 CV 中,並將它們全部合併到每個類別的單一 CV 中。如果類別有多個 ADJUST 區塊,這將使呼叫速度更快。
存取器產生器屬性
要求為欄位產生存取器方法的屬性。可能是 :reader
和 :writer
。
class X {
field $name :reader;
}
等同於
class X {
field $name;
method name { return $name; }
}
元程式設計
元程式設計 API 的延伸(目前由 RFC0022 提議),增加了對類別、方法、欄位、ADJUST 區塊和其他類別相關詳細資料的了解。
延伸自訂
核心外模組與類別系統互動的方式,包括它們提供新的類別或欄位屬性的能力。
Paul Evans
Bartosz Jarzyna