Perl類的繼承是通過@ISA數(shù)組實(shí)現(xiàn)的矩欠。@ISA數(shù)組不需要在任何包中定義猾蒂,然而,一旦它被定義隐孽,Perl就把它看作目錄名的特殊數(shù)組癌椿。它與@INC數(shù)組類似,@INC是包含文件的尋找路徑菱阵。@ISA數(shù)組含有類(包)名踢俄,當(dāng)一個(gè)方法在當(dāng)前包中未找到時(shí)就到@ISA中的包去尋找。@ISA中還含有當(dāng)前類繼承的基類名晴及。
類中調(diào)用的所有方法必須屬于同一個(gè)類或@ISA數(shù)組定義的基類褪贵。如果一個(gè)方法在@ISA數(shù)組中未找到,Perl就到AUTOLOAD()子程序中尋找抗俄,這個(gè)可選的子程序在當(dāng)前包中用sub定義。若使用AUTOLOAD子程序世舰,必須用use Autoload;語句調(diào)用autoload.pm包动雹。AUTOLOAD子程序嘗試從已安裝的Perl庫(kù)中裝載調(diào)用的方法。如果AUTOLOAD也失敗了跟压,Perl再到UNIVERSAL類做最后一次嘗試胰蝠,如果仍失敗,Perl就生成關(guān)于該無法解析函數(shù)的錯(cuò)誤震蒋。
perl中有兩種不同地面向?qū)ο缶幊痰膶?shí)現(xiàn):
1.基于匿名哈希表的方式茸塞,每個(gè)對(duì)象實(shí)例的實(shí)質(zhì)就是指向匿名哈希表的引用。在這個(gè)匿名哈希表中查剖,存儲(chǔ)來所有的實(shí)例屬性钾虐。
2.基于數(shù)組的方式。定義一個(gè)類時(shí)笋庄,我們將為每一個(gè)實(shí)例創(chuàng)建一個(gè)數(shù)組效扫,而每一個(gè)對(duì)象實(shí)例的實(shí)質(zhì)就是指向這些數(shù)組中某一行索引的引用倔监。在這些數(shù)組中,存儲(chǔ)著所有的實(shí)例屬性菌仁。
面向?qū)ο蟮母拍?##
實(shí)例:一個(gè)對(duì)象的實(shí)例化實(shí)現(xiàn)浩习。
標(biāo)識(shí):每個(gè)對(duì)象的實(shí)例都需要一個(gè)可以唯一標(biāo)識(shí)這個(gè)實(shí)例的標(biāo)記。
實(shí)例屬性:一個(gè)對(duì)象就是一組屬性的集合济丘。
實(shí)例方法:所有存取或者更新對(duì)象某個(gè)實(shí)例一條或者多條屬性的函數(shù)集合谱秽。
類屬性:屬于一個(gè)類中所有對(duì)象的屬性,不會(huì)只在某個(gè)實(shí)例上發(fā)生變化摹迷。
類方法:那些無需特定的對(duì)象實(shí)例就能工作的從屬于類的函數(shù)疟赊。
基于匿名散列表的實(shí)現(xiàn)###
首先,我們需要定義一個(gè)匿名散列表泪掀,并用一個(gè)引用指向這個(gè)匿名散列表听绳,如清單1所示,我們定義了一個(gè)初始化函數(shù)來封裝這個(gè)匿名散列表的初始化過程异赫。這個(gè)函數(shù)接受參數(shù)作為初始值椅挣,并引用這些初始值初始化其內(nèi)部包含的匿名哈希表,并且返回一個(gè)指向這個(gè)匿名哈希表的引用塔拳。
package Person;
sub new{
my ($name,$age) = @_;
my $_self = {
"name" => $name,
"age" => $age,
};
return $_self;
}
my $p_obj1 = Person->new('Tony',23);
my $p_obj2 = Person->new('Tommy',30);
print "Person1's name : " . $p_obj1->{name} . "age: " . $p_obj1->{age} . ".\n" ;
print "Person2's name : " . $p_obj2->{name} . "age: " . $p_obj2->{age} . ".\n";
此方案有一個(gè)致命的問題鼠证,perl的編譯器并不知道new函數(shù)所返回的指向匿名哈希表的引用屬于哪個(gè)類。所以靠抑,在清單2中量九,如果要使用類中的實(shí)例方法,只能直接標(biāo)出方法所屬的類的名字颂碧,并將引用作為方法的第一個(gè)參數(shù)傳遞給它荠列。
package Person;
...
sub change_name{
my ($self,$new_name) = @_;
$self->{name} = $new_name;
}
my $p_obj = Person->new("tony",23);
print "Person's name :" . $p_obj->{name} . ".\n";
Person::change_name($p_obj,"Tommy");
print "Person's new name : " . $p_obj->{name}. ".\n";
解決這個(gè)問題的關(guān)鍵是引入bless。bless以一個(gè)普通的指向數(shù)據(jù)結(jié)構(gòu)的引用為參數(shù)载城,它將會(huì)把那個(gè)數(shù)據(jù)結(jié)構(gòu)(不是引用本身)標(biāo)記為屬于某個(gè)特定的包肌似。這樣就賦予了這個(gè)匿名哈希表的引用以多態(tài)的能力。同時(shí)诉瓦,我們使用箭頭記號(hào)來直接調(diào)用那些實(shí)例方法川队。
以下清單中的“bless($self)”,將指向一個(gè)匿名哈希表的引用標(biāo)記為屬于當(dāng)前包睬澡,也就是package Person固额。所以當(dāng)Perl看到"$p_obj->change_name($name)"時(shí),它會(huì)決定$p_obj屬于package Person煞聪。
Perl會(huì)如下所示地調(diào)用這個(gè)函數(shù)斗躏,“Person::change_name($p_obj,$name)”,和清單2的第一種實(shí)現(xiàn)一樣,換而言之昔脯,如果使用箭頭的方式調(diào)用一個(gè)函數(shù)瑟捣,箭頭左邊的那個(gè)對(duì)象將作為相應(yīng)子程序的第一個(gè)參數(shù)馋艺,Perl的實(shí)例方法本質(zhì)就是一個(gè)第一個(gè)參數(shù)碰巧為對(duì)象引用的子程序。
package Person
sub new{
my $self = {};
shift;
my ($name,$age) = @_;
$self->{name} = $name;
$self->{age} =$age;
bless($self);
return $self;
}
sub change_name{
my $self = shift;
my $name = shift;
$self->{name} = $name;
}
my $p_obj = Person->new("David", 27);
print "Name:" . $p_obj->{name} . ".\n";
$p_obj->change_name("Tony");
print "Name: " . $p_obj->{name}. "\n";
Perl的這種調(diào)用相應(yīng)模塊函數(shù)的能力被稱作運(yùn)行時(shí)聯(lián)編迈套。調(diào)用過new方法之后捐祠,返回一個(gè)匿名哈希表的引用,并且包含相應(yīng)類的名字桑李。
Perl中并沒有針對(duì)類屬性和類方法的特定語法踱蛀。類屬性只是包中的全局變量,而類方法則是不依賴于任何特定實(shí)例的子程序贵白。以下清單是一個(gè)關(guān)于類屬性和類方法的例子率拒。與實(shí)例方法不同,我們使用Person::calculate_person_number()的形勢(shì)來調(diào)用類方法禁荒。這樣的話猬膨,指向匿名哈表的引用將不會(huì)做為第一個(gè)調(diào)用參數(shù)傳入,我們與不需要在包的子程序附加處理傳入引用的代碼呛伴。
清單4:基于匿名哈希表的面向?qū)ο缶幊讨械念惙椒ㄅc類屬性
my $person_number = 0;
...
sub new{
$person_number++;
}
sub calculate_person_number{
return $person_number;
}
my $p_obj1 = Person->new("David", 27);
my $p_obj2 = Person->new("Tonny", 27);
my $person_number = Person::calculate_person_number();
print "We have ". $person_number . " persons in all.\n";
基于匿名哈希表的方法中的繼承###
Perl允許一個(gè)模塊在一個(gè)特殊的名為@ISA的數(shù)組中指定一組其他模塊的名稱勃痴。當(dāng)在模塊中找不到某個(gè)實(shí)例方法時(shí),它就為檢查那個(gè)模塊 的@ISA是否被初始化热康。如果已經(jīng)初始化了 沛申,他就會(huì)檢查其中的某個(gè)模塊是否支持這個(gè)“缺少”的函數(shù)。如果它按照深度優(yōu)先的層次結(jié)構(gòu)搜索@ISA數(shù)組并且發(fā)現(xiàn)同名的方法姐军,它會(huì)調(diào)用第一個(gè)被發(fā)現(xiàn)的同名方法并且將控制權(quán)交給它铁材,我們利用perl的這個(gè)特性實(shí)現(xiàn)繼承。
考慮這樣一個(gè)類的層次奕锌,我們定義一個(gè)Employee類著觉,繼承于基站類的Person,如清單5所示惊暴。
我們將類名Person放入包Employee的ISA數(shù)組中饼丘,這樣調(diào)用一個(gè)在包Employee中沒有定義的函數(shù)時(shí),Perl編譯器就會(huì)自動(dòng)在Person類中尋找這個(gè)函數(shù)缴守,當(dāng)用戶調(diào)用new函數(shù)初始化一個(gè)employee實(shí)例對(duì)象的時(shí)候,Employee的new函數(shù)會(huì)在內(nèi)部調(diào)用它的基類的new函數(shù)镇辉,并且返回一個(gè)包含部分以初始化的基類實(shí)例屬性的匿名哈希表屡穗。接著Employee的new函數(shù)將繼續(xù)執(zhí)行new函數(shù)的剩余代碼,完成屬于Employee自身的初始化工作忽肛,為Employee剩余的實(shí)例屬性賦值村砂。
清單5.基于匿名哈希表的面向?qū)ο缶幊讨械睦^承
use Person;
package Employee;
@ISA = qw( Person );
sub new{
shift;
my ($name,$age,$salary) = @_;
my $self = Person->new($name,$age);
$self->{salary} =$salary;
bless ($self);
return $self;
}
sub change_salary{
my $self = shift;
my $new_salary = shift;
$self->{salary} = $new_salary;
}
my $p_obj =Employee->new("Tonny", 28,10000);
print "Name: " . $p_obj->{name} . "Age: " . $p_obj->{age} .
",Salary: " . $p_obj->{salary} . " . \n";
$p_obj->change_name("Tommy");
$p_obj->change_salary(13000);