bless 方法
method bless(*%attrinit) returns Mu:D
相比 new方法來說更低層級(jí)別的對(duì)象構(gòu)造方法。
創(chuàng)建一個(gè)和調(diào)用者同類型的新對(duì)象, 使用具名參數(shù)來初始化屬性, 并返回創(chuàng)建后的對(duì)象。
在自定義構(gòu)造函數(shù)時(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ù), 記得它會(huì)讓子類繼承變得更困難。
use v6;
# bless 的原理
class Dog {
has $.name;
my $.counter; # 類方法
# 重寫 new 方法, 使用位置參數(shù)創(chuàng)建實(shí)例
method new ($newName) {
$.counter++;
self.bless(name => $newName);
}
}
my $dog = Dog.new("yayaya");
say $dog.name; # yayaya
say Dog.counter; # 1
讓我們創(chuàng)建一個(gè)對(duì)象
在 Perl 6 中創(chuàng)建一個(gè)對(duì)象相當(dāng)容易。 作為類的作者你真的不必關(guān)心(至少在最簡單的情況下), 你從 Mu 類繼承了一個(gè)默認(rèn)的構(gòu)造函數(shù)。作為類的使用者, 你僅僅寫出 MyClass.new(attrib1 => $value1) 就能創(chuàng)建一個(gè) MyClass類的對(duì)象, 同時(shí)初始化了一個(gè)公開的屬性 attrib1。
運(yùn)行初始化代碼
如果你想在對(duì)象創(chuàng)建中運(yùn)行某些初始化代碼, 那么你一點(diǎn)兒也沒有必要?jiǎng)佑?new方法。使用 BUILD 子方法:
class C {
submethod BUILD {
say "創(chuàng)建一個(gè) C 的實(shí)例";
}
}
C.new(); # 創(chuàng)建一個(gè) C 的實(shí)例
BUILD submethod 由構(gòu)造函數(shù)自動(dòng)調(diào)用, 并且可以處理任何必要的初始化。BUILD submethod 也能接收用戶傳遞給 new() 方法的具名參數(shù)。
(以防你疑惑, submethod 是不能被子類繼承的方法。)
因?yàn)?BUILD 運(yùn)行在尚未完全構(gòu)建好的對(duì)象上, 屬性只有在被聲明為具名參數(shù)的時(shí)候才可以被訪問:
submethod BUILD(:$!attr1, :$!attr2) {
# 這兒可以使用 $!attr1 和 $!attr2
}
該語法也自動(dòng)的使用和 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');
所以下面的兩個(gè)類聲明, 表現(xiàn)一樣:
class D {
has $.x;
}
# and
class D {
has $!x; # 私有屬性
submethod BUILD(:$!x) {} # 允許 D.new( x => $x )
method x() {$!x} # accessor
}
這也解釋了 has $.x 等價(jià)于 has $!x 加上 accessor 的原理。
自定義構(gòu)造函數(shù)
假如你對(duì)具名參數(shù)不感冒, 而你想自定義一個(gè)接收一個(gè)強(qiáng)制位置參數(shù)的構(gòu)造函數(shù)。那樣你就需要自定義 new方法。要?jiǎng)?chuàng)建一個(gè)對(duì)象, 被重寫的 new 方法中必須調(diào)用 self.bless:
class C {
has $.size;
method new($x) {
self.bless(*, size => 2 * $x);
}
}
say C.new(3).size; # 接收一個(gè)位置參數(shù), 打印出 6
bless的第一個(gè)參數(shù) *****號(hào)告訴它創(chuàng)建一個(gè)空對(duì)象自身。
如果你想開啟額外的具名參數(shù), 那很容易:
class C {
has $.size;
method new($x, *%remaining) {
self.bless(*, size => 2 * $x, |%remaining);
}
}
注意, 這兩個(gè)概念(自定義 new() 和 BUILD() (sub)methods) 是正交的; 你一次可以使用它倆, 它倆能和諧共處。
屬性的默認(rèn)值
為屬性提供默認(rèn)值的最方便的方式是在聲明屬性的時(shí)候?yàn)閷傩蕴峁┠J(rèn)值:
class Window {
has $.height = 600;
has $.width = $.height * 1.618;
...
}
默認(rèn)值只會(huì)用在底層屬性沒有被 new 或 BUILD接觸的時(shí)候使用。
理解對(duì)象初始化
假如你有一個(gè)類 C 繼承自類 B, 那么創(chuàng)建一個(gè)類 C 的對(duì)象的處理看起來是這樣:

用戶調(diào)用 C.new, 這反過來調(diào)用 self.bless(*, |args)。bless 方法創(chuàng)建了一個(gè)新的存儲(chǔ)新創(chuàng)建對(duì)象的 P6Opaque 對(duì)象。這就是調(diào)用上圖中的 CREATE。
分配完存儲(chǔ)空間和屬性初始化之后, new把控制權(quán)傳給BUILDALL(順帶傳遞所有的具名參數(shù)), 這反過來會(huì)從層級(jí)樹的頂端開始, 調(diào)用繼承層級(jí)樹上所有類的 BUILD 方法, 最后調(diào)用類 C 的 BUILD 方法。
這樣的設(shè)計(jì)允許你花費(fèi)最少的力氣來替換初始化的一部分, 尤其是自定義 new 和 BUILD 方法會(huì)很容易寫。
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');
打?。?/p>
調(diào)用了 B 的 BUILD, 我叫 Lucy
調(diào)用了 C 的 BUILD, 我叫 Camelia, 我爸爸是 Lucy