第十二章 对象(上)
对象是一个数据结构, 带有一些行为。我们通常把这些行为成为对象的直接动作,
类和实例方法 通过第一个参数区分,对于 Perl 而言,方法就是方法,只是由其第一个参数的类型来区分。
一个类可以从父类中继承方法,父类也叫基类或者超级类。
原则--所有对象的访问都应该只通过方法,
12.2 Perl 的对象系统
一个对象只不过是一个引用...恩,就是引用。
因为引用令独立的标量代表大量的数据,
一个类只是一个包
一个包当作一个类——通过使用包的子过程来执行类的方法,以及通过使用包的 变量来保存类的全局数据。通常,
使用一个模块来保存一个或者更多个类。
一个方法只是一个子过程
12.3 方法调用
如果用一个类调用该方法,那个参数 将会是类的名字。如果用一个对象调用方法,那个参数就是对象的引用。
对于类方法而言,调用者是包的名字。对于一个实例方法,调用者是 调用
者是一个声明对象的引用
12.3.1 使用箭头操作符的方法调用
INVOCANT->METHOD(LIST)
INVOCANT->METHOD
INVOCANT 是一个包的 名字的时候,我们把那个被调用的METHOD 看作类方法。
比如,使用类方法 summon 的构造一个类,然后在生成的对象上调用实例方法 speak,你 可以这么说:
$mage = Wizard->summon("Gandalf"); # 类方法
$mage->speak("friend") # 实例方法
12.3.2 使用间接对象的方法调用
12.3.4 引用包的类
12.4 构造对象
所有对象都是引用,但不是所有引用都是对象
把一个引用和一个包名字标记起来(因此也和 包中的类标记起来了,因为一个类就是一个包)
的动作被称作赐福(blessing),你可以把 赐福(bless)看作把一个引用转换成一个对象
bless 函数接收一个或者两个参数。第一个参数是一个引用,而第二个是要把引用赐福 (bless)成的包。如果忽
略第二个参数,则使用当前包。
Vsftp:/root/perl/8# cat Critter.pm
package Critter;
sub new {
my $self = {'a'=>11,'b'=>22}; # 指向一个空的匿名散列
bless $self, "Critter"; # 把那个散列作成一个 Critter 对象
return $self; # 返回新生成的 Critter
};
1;
Vsftp:/root/perl/8# cat a1.pl
unshift(@INC,"/root/perl/8");
use Critter;
use Data::Dumper;
$pet = Critter->new;
print Dumper($pet);
print "\n";
print $pet->{"a"};
print "\n";
Vsftp:/root/perl/8# perl a1.pl
$VAR1 = bless( {
'a' => 11,
'b' => 22
}, 'Critter' );
11
12.4.1 可继承构造器
和所有方法一样,构造器只是一个子过程,但是我们不把它看做一个子过程。
在这里例子里,我们总是把它当作一个方法来调用
---类方法,因为调用者是一个包名字。
方法调用和普通的子过程调用有两个区别。
12.4.2 初始器
Vsftp:/root/perl/8# cat a2.pl
sub fun1 {
print "\@_ is @_\n";
my $a1=shift;
print "\$a1 is $a1\n";
print "\@_ is @_\n";
};
fun1(1,2,43,434);
Vsftp:/root/perl/8# perl a2.pl
@_ is 1 2 43 434
$a1 is 1
@_ is 2 43 434
sub new {
my $invocant = shift; ##从@_中取出第一个元素 类的名字
my $class = ref($invocant) || $invocant;
my $self = { @_ }; # 剩下的参数变成属性
bless($self, $class); # 给予对象性质
return $self;
}
Vsftp:/root/perl/8# cat Horse.pm
package Horse;
sub new {
print "\@_ is @_\n";
my $invocant = shift; ##从@_中取出第一个元素 类的名字
print "\$invocant is $invocant\n";
print "\@_ is @_\n";
my $class = ref($invocant) || $invocant;
my $self = { @_ }; # 剩下的参数变成属性
bless($self, $class); # 给予对象性质
return $self;
};
1;
Vsftp:/root/perl/8# cat a3.pl
unshift(@INC,"/root/perl/8");
use Horse;
use Data::Dumper;
$stallion = Horse->new(color => "black");
print Dumper($ed);
Vsftp:/root/perl/8# perl a3.pl
@_ is Horse color black
$invocant is Horse
@_ is color black
$VAR1 = undef;
12.5 类继承
对Perl的对象系统剩下的内容,从一个类继承另外一个类并不要给这门语言增加特殊的语法。
当你调用一个方法,如果perl 在调用者的包里找不到这个子过程,那么它就检查@ISA数组
Horse类继承Critter (父类 或称为基类):
Vsftp:/root/perl/9# cat Horse.pm
package Horse;
our @ISA = "Critter";
sub new {
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my $self = {
color => "bay",
legs => 4,
owner => undef,
@_, # 覆盖以前的属性
};
return bless $self, $class;
};
sub sum_arr {
$self=shift;
my $a=shift;
my $b=shift;
return $a + $b + 7;
};
1;
Vsftp:/root/perl/9# cat Critter.pm
package Critter;
sub new {
my $self = {};
my $invocant = shift;
my $class = ref($invocant) || $invocant;
my ($name)=@_;
my $self = {
"name" =>$name
};
bless $self, $class; # Use class name to bless() reference
return $self;
};
sub sum {
$self=shift;
my $a=shift;
my $b=shift;
return $a + $b;
};
1;
Vsftp:/root/perl/9# cat a1.pl
unshift(@INC,"/root/perl/9");
use Horse;;
use Critter;
use Data::Dumper;
$ed = Horse->new; # 四腿湾马
print $ed->sum_arr(4,5);
print "\n";
print $ed->sum(4,5);
print "\n";
Vsftp:/root/perl/9# perl a1.pl
16
9
你现在应该可以在原先Critter 使用的任何地方使用Horse类或者对象了
当你调用一个方法的时候,如果 Perl 在调用者的包里找不到这个子过程, 那么它就检查 @ISA 数组(
Perl 是这样实现继承的:一个包的 @ISA 数组里的每个元素都保存另外一个包的名字,
package Horse;
our @ISA = "Critter";
把 Horse 类变成 Critter 类的子类
Critter 是父类或者称为基类
假设你在 $steed 里有一个 Horse 对象,并且在他上面调用了一个 move:
$steed->move(10);
因为 $steed 是一个 Horse,Perl 对该方法的第一个选择是 Horse::move 子过程。如果 没有,Perl 先询问
@Horse::ISA 的第一个元素,而不是生成一个运行时错误,这样将导致 查询到 Critter 包里,并找到
Critter::move。如果也没有找到这个子过程,而且 Critter 有自己的 @Critter::ISA 数组,那么继续查询那里面
的父类,看看有没有一个 move 方法,如此类推直到上升到继承级别里面一个没有 @ISA 的包