第十二章 对象(上) 对象是一个数据结构, 带有一些行为。我们通常把这些行为成为对象的直接动作, 类和实例方法 通过第一个参数区分,对于 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 " "; print $pet->{"a"}; print " "; 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 @_ "; my $a1=shift; print "$a1 is $a1 "; print "@_ is @_ "; }; 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 @_ "; my $invocant = shift; ##从@_中取出第一个元素 类的名字 print "$invocant is $invocant "; print "@_ is @_ "; 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 " "; print $ed->sum(4,5); print " "; 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 的包