bless 方法
method bless(*%attrinit) returns Mu:D
相比 new
方法來說更低層級別的對象構(gòu)造方法驴党。
創(chuàng)建一個和調(diào)用者同類型的新對象, 使用具名參數(shù)來初始化屬性, 并返回創(chuàng)建后的對象寻拂。
在自定義構(gòu)造函數(shù)時可以使用該方法:
class Point {
has $.x;
has $.y;
# multi 是可選的
multi method new($x, $y) {
self.bless(:$x, :$y);
}
}
# 重寫構(gòu)造函數(shù)后, 不需要傳具名參數(shù)了
my $p = Point.new(-1, 1);
say $p.x; # -1
雖然你可以自定義構(gòu)造函數(shù), 記得它會讓子類繼承變得更困難精置。
use v6;
# bless 的原理
class Dog {
has $.name;
my $.counter; # 類方法
# 重寫 new 方法, 使用位置參數(shù)創(chuàng)建實例
method new ($newName) {
$.counter++;
self.bless(name => $newName);
}
}
my $dog = Dog.new("yayaya");
say $dog.name; # yayaya
say Dog.counter; # 1
讓我們創(chuàng)建一個對象
在 Perl 6 中創(chuàng)建一個對象相當(dāng)容易餐屎。 作為類的作者你真的不必關(guān)心(至少在最簡單的情況下), 你從 Mu
類繼承了一個默認(rèn)的構(gòu)造函數(shù)艳吠。作為類的使用者, 你僅僅寫出 MyClass.new(attrib1 => $value1)
就能創(chuàng)建一個 MyClass
類的對象, 同時初始化了一個公開的屬性 attrib1主卫。
運行初始化代碼
如果你想在對象創(chuàng)建中運行某些初始化代碼, 那么你一點兒也沒有必要動用 new
方法。使用 BUILD 子方法:
class C {
submethod BUILD {
say "創(chuàng)建一個 C 的實例";
}
}
C.new(); # 創(chuàng)建一個 C 的實例
BUILD submethod 由構(gòu)造函數(shù)自動調(diào)用, 并且可以處理任何必要的初始化虾宇。BUILD submethod 也能接收用戶傳遞給 new()
方法的具名參數(shù)搓彻。
(以防你疑惑, submethod 是不能被子類繼承的方法。)
因為 BUILD 運行在尚未完全構(gòu)建好的對象上, 屬性只有在被聲明為具名參數(shù)的時候才可以被訪問:
submethod BUILD(:$!attr1, :$!attr2) {
# 這兒可以使用 $!attr1 和 $!attr2
}
該語法也自動的使用和 new
方法同名的具名參數(shù)的值初始化屬性 嘱朽。
use v6;
class Cat {
has $.fullname;
has $.nickname;
submethod BUILD(:$!fullname, :$!nickname) {
say "造了一只貓, 它的全名是 $!fullname, 它的昵稱是 $!nickname";
}
}
# 造了一只貓, 它的全名是 Camelia, 它的昵稱是 Rakudo Star
Cat.new(fullname => 'Camelia', nickname => 'Rakudo Star');
所以下面的兩個類聲明, 表現(xiàn)一樣:
class D {
has $.x;
}
# and
class D {
has $!x; # 私有屬性
submethod BUILD(:$!x) {} # 允許 D.new( x => $x )
method x() {$!x} # accessor
}
這也解釋了 has $.x
等價于 has $!x
加上 accessor 的原理旭贬。
自定義構(gòu)造函數(shù)
假如你對具名參數(shù)不感冒, 而你想自定義一個接收一個強制位置參數(shù)的構(gòu)造函數(shù)。那樣你就需要自定義 new
方法搪泳。要創(chuàng)建一個對象, 被重寫的 new 方法中必須調(diào)用 self.bless
:
class C {
has $.size;
method new($x) {
self.bless(*, size => 2 * $x);
}
}
say C.new(3).size; # 接收一個位置參數(shù), 打印出 6
bless
的第一個參數(shù) *****號告訴它創(chuàng)建一個空對象自身稀轨。
如果你想開啟額外的具名參數(shù), 那很容易:
class C {
has $.size;
method new($x, *%remaining) {
self.bless(*, size => 2 * $x, |%remaining);
}
}
注意, 這兩個概念(自定義 new() 和 BUILD() (sub)methods) 是正交的; 你一次可以使用它倆, 它倆能和諧共處。
屬性的默認(rèn)值
為屬性提供默認(rèn)值的最方便的方式是在聲明屬性的時候為屬性提供默認(rèn)值:
class Window {
has $.height = 600;
has $.width = $.height * 1.618;
...
}
默認(rèn)值只會用在底層屬性沒有被 new
或 BUILD
接觸的時候使用森书。
理解對象初始化
假如你有一個類 C 繼承自類 B, 那么創(chuàng)建一個類 C 的對象的處理看起來是這樣:
用戶調(diào)用 C.new
, 這反過來調(diào)用 self.bless(*, |args)
靶端。bless 方法創(chuàng)建了一個新的存儲新創(chuàng)建對象的 P6Opaque 對象谎势。這就是調(diào)用上圖中的 CREATE凛膏。
分配完存儲空間和屬性初始化之后, new
把控制權(quán)傳給BUILDALL(順帶傳遞所有的具名參數(shù)), 這反過來會從層級樹的頂端開始, 調(diào)用繼承層級樹上所有類的 BUILD 方法, 最后調(diào)用類 C 的 BUILD 方法。
這樣的設(shè)計允許你花費最少的力氣來替換初始化的一部分, 尤其是自定義 new 和 BUILD 方法會很容易寫脏榆。
use v6;
class B {
has $.name;
submethod BUILD(:$!name) {
say "調(diào)用了 B 的 BUILD, 我叫 $!name"
}
}
class C is B {
has $.nickname;
submethod BUILD(:$!nickname, :$name) {
say "調(diào)用了 C 的 BUILD, 我叫 $!nickname, 我爸爸是 $name"
}
method new(:$nickname) {
self.bless(nickname => 'Camelia', name => 'Lucy');
}
}
my $c = C.new(nickname => 'HANMEIMEI');
打硬痢:
調(diào)用了 B 的 BUILD, 我叫 Lucy
調(diào)用了 C 的 BUILD, 我叫 Camelia, 我爸爸是 Lucy