本系列:html
第3篇依赖于第2篇,第2篇依赖于1篇。java
如今有父类Animal,子类Horse,它们的代码分别以下:程序员
lib/Animal.pm中:编程
#!/usr/bin/env perl use strict; use warnings; package Animal; sub speak { my $class = shift; print "a $class goes ",$class->sound(),"!\n"; } sub sound { die 'You have to define sound() in a subclass'; } 1;
lib/Horse.pm中:数组
#!/usr/bin/env perl use strict; use warnings; package Horse; use parent qw(Animal); sub sound { "neigh" } 1;
一个perl程序speak.pl文件:数据结构
#!/usr/bin/env perl use strict; use warnings; use lib "lib"; use Horse; Horse->speak();
执行上面的speak.pl,将输出:闭包
a Horse goes neigh
上面使用Horse->speak()
的方式调用speak()方法,它首先调用到父类Animal中的speak(由于Horse类中没有重写该方法),而后Animal中的speak又从新回调Horse类中的sound()。less
这个speak是全部Horse都共享的,若是想要定义每一个Horse对象都私有的数据呢?好比为每一个Horse对象命名。这里Horse的名字就是Horse类的实例数据(在其它编程语言中常称之为成员变量),是每一个对象独有的。编程语言
在Perl的面向对象编程中,一个对象表示的就是一个对内置类型的引用,好比标量引用、数组引用、hash引用。也就是说,所谓对象就是一个指向内置数据结构的引用,这个数据结构能够认为是每一个对象私有的成员变量。ide
强烈建议使用hash引用的方式,不过此处先以标量引用的方式开始本文的介绍。
修改speak.pl文件:
#!/usr/bin/env perl use strict; use warnings; use lib "lib"; use Horse; my $name = "baima"; my $bm_horse = \$name; bless $bm_horse,'Horse';
bless的语法为:
bless REFERENCE,CLASS;
它表示为CLASS类设置一个惟一标识符,并返回这个惟一标识符,这个惟一标识符是一个数据结构的引用,这个惟一标识符也被称为对象。也就是说,对象就是一个引用,因此咱们经常会使用my $obj = bless REF,CLASS;
来返回一个对象。另外一方面,bless表示将一个数据结构和类进行关联,表示这个数据结构(也许是空的,也许是通过必定初始化的)已经附加在类上,当建立这个类的实例(对象)时,它将返回一个引用,对这个数据结构的引用,换句话说,这个对象就已经拥有了这个数据结构。
以下图所示:
上面的Class是类,引用变量$obj_ref
是这个类的惟一标识符,它指向一个数据结构,这个数据结构是这个类的属性。使用Horse进行具体化,Horse是类,$bm_horse
是这个类的惟一标识符,这个引用指向值为"baima"的标量数据结构,因此$bm_horse
是一个对象,"baima"就是它独有的属性。
也就是说,bless $bm_horse,'Horse';
已经建立了一个名为$bm_horse
的对象,而"baimai"这个属性是只属于这一个对象的数据。
bless生成一个引用后,这个引用是类的实例,能够经过这个引用变量去调用类的方法。
#!/usr/bin/env perl use strict; use warnings; use lib "lib"; use Horse; my $name = "baima"; my $bm_horse = \$name; bless $bm_horse,'Horse'; print $bm_horse->sound(),"\n";
上面经过对象去调用类方法,它首先搜索出sound()在何处(即类中仍是父类中),而后将参数传递给sound()。传递的参数列表中,第一个参数是实例的名称,也就是$bm_horse
,就像经过类名去调用类方法时,传递的第一个参数是类名同样。因此,下面两个是等价的:
$bm_horse->sound(); Horse::sound($bm_horse);
实际上,bless最初的目的就是经过一个引用来关联正确的类,以便perl能正确地找到所调用的方法,免去经过硬编码类名的麻烦。
再调用speak()试试:
$bm_horse->speak();
它将输出:
a Horse=SCALAR(0xc78610) goes neigh!
这是由于$bm_horse
是一个指向标量数据结构的引用,speak()方法中将其赋值给$class
,$class
也仍然是引用,并且speak()中并无去解除这个引用,因此如此输出。至于解决方法,留待后文。
由于实例的名称是每一个对象的惟一标识符,而如今能够经过传递给方法的第一个参数获取实例的名称,借此名称,能够进一步地获取到该实例的其它数据。
如今,在lib/Horse.pm文件中添加一个name方法:
sub name { my $self = shift; $$self; }
注意上面$$self
,由于$self
是对象名,而对象名老是一个引用变量,所以将其解除引用。
而后在speak.pl中调用这个方法:
#!/usr/bin/env perl use strict; use warnings; use lib "lib"; use Horse; my $name = "baima"; my $bm_horse = \$name; bless $bm_horse,'Horse'; print $bm_horse->name()," says ",$bm_horse->sound(),"\n";
该print将输出:
baima says neigh
perl中几乎都使用$self
做为类名或对象名的代名词,就像java中的"this"同样。实际上,你可使用任何变量名称,但约定俗成地,你们都喜欢用self。
前面构造Horse对象是在独立的speak.pl文件中实现的,这样生成Horse对象的方式是手动的,是彻底私有的,构造对象时的实例数据(即name属性)也是彻底暴露的。当在多个文件中都这样构造Horse对象,早晚会出错。
因而,在类文件lib/Horse.pm中定义一个构造方法new(),每次要构造Horse对象的时候只需调用这个方法便可。
#!/usr/bin/env perl use strict; use warnings; package Horse; use parent qw(Animal); sub new { my $class = shift; my $name = shift; belss \$name,$class; } sub name { my $self = shift; $$self; } sub sound { "neigh" } 1;
上面在Horse.pm中定义了一个new()方法,该方法里面包含了bless语句,且做为new()方法的最后一个语句,表示构造一个对象并返回这个对象的惟一标识符:引用变量表示的对象名。所以,这个new()方法被称之为构造方法:用于构造该类的实例。
方法名new()能够随意,例如hire(),named()等均可以,但面向对象编程语言中,基本上都使用new这个词语来表示建立新对象,因此,也建议采用约定俗成的new(),若是使用其它方法名做为构造方法,请作好注释。
如今,只要调用Horse中的这个new()方法,就表示在当前包中构建一个Horse的实例(bless的返回值):
my $bm_horse = Horse->new("baima");
注意,bless返回的是对象引用,因此赋值给变量$bm_horse
,这时$bm_horse
将表明这个对象,是这个对象的惟一标识符。
上面调用new()的过程当中,首先找到类方法new(),而后传递参数列表('Horse',"baima")
,new()方法中,bless将baima这个数据结构附加到Horse类中,并返回指向该数据结构的引用。之后,经过$bm_horse
就能找到这个数据结构,由于这个数据结构是对象$bm_horse
的实例数据。
在上面lib/Horse.pm中的构造方法new()中是否有Horse所特有的个性内容?彻底没有。不管是Horse、Cow仍是Sheep的构造方法都是通用的,因此将共性的代码抽取到父类Animal中。
lib/Animal.pm中:
#!/usr/bin/env perl use strict; use warnings; package Animal; sub new { my $class = shift; my $name = shift; bless \$name,$class; } sub name { my $self = shift; $$self; } sub speak { my $class = shift; print "a $class goes ",$class->sound(),"!\n"; } sub sound { die 'You have to define sound() in a subclass'; } 1;
lib/Horse.pm中:
#!/usr/bin/env perl use strict; use warnings; package Horse; use parent qw(Animal); sub sound { "neigh" } 1;
如此一来,不管是Horse、Cow仍是Sheep都继承父类Animal中的构造方法new()以及name()。注意,上面name()方法也抽取到了Animal类中,由于它也是共性的,不过本小节暂时用不到该方法,下一小节会修改该方法。
如今,在speak.pl文件中构建一个Horse对象:
my $bm_horse = Horse->new("baima");
而后经过这个对象调用speak方法:
$bm_horse->speak();
它将输出:
a Horse=SCALAR(0xc78610) goes neigh!
这个实验前文已经验证过了。之因此会如此,是由于传递给speak()的第一个参数是对象的引用变量,而speak()中并无去解除这个引用。再次看看speak()的代码:
sub speak { my $class = shift; print "a $class goes ",$class->sound(),"!\n"; }
speak()中的$class期待的实际上是一个类名,而不是对象名,由于类名是具体的字符串,而非引用变量。例如,使用Horse->speak()
就不会出现上面的问题。
如何解决父类中的这种问题,使其能同时处理类名和对象名?
为了让父类中的方法能同时处理类名和对象名,能够加入一个额外的方法对类名和对象名进行判断。如何判断是类名仍是对象名?只需使用ref便可,若是ref能返回一个值,表示这是一个引用,说明这是对象,ref返回false,则说明这不是引用,也就是类名。
以前由于name()方法由于共性的缘由被抽取到Animal.pm后并无使用过,这里派上用场了。
lib/Animal.pm中:
#!/usr/bin/env perl use strict; use warnings; package Animal; sub new { my $class = shift; my $name = shift; bless \$name,$class; } sub name { my $self = shift; ref $self ? $$self : "an unamed Class $self"; # 修改此行 } sub speak { my $class = shift; print $class->name()," goes ",$class->sound(),"!\n"; # 调用name()方法 } sub sound { die 'You have to define sound() in a subclass'; } 1;
这样speak()就变得共性化,既能处理类名,也能处理对象名。
my $bm_horse = Horse->new("baima"); $bm_horse->speak(); # 传递对象名 Horse->speak(); # 传递类名
将输出以下结果:
baima goes neigh! an unamed Class Horse goes neigh!
之因此加入新的方法,是由于在speak()中类名和对象名是相互独立的,也就是没法共性的,要么是类名,要么是对象名。为了让一段代码共性化,解决方法就是添加额外的代码将非共性内容化解掉,这些额外的代码能够直接加在speak()内部,也能够放进一个新定义的方法中,而后在speak()中调用这个方法。这是一种编程思想。
常常地,perl使用hash做为对象的数据结果,这个数据结构中能够存储不一样的数据、引用,甚至是对象,其中hash的key常做为实例数据(成员变量)。
再次说明,perl面向对象时最经常使用的对象数据结构是hash,但标量、数组也同样能够,至少不多用。
想要使用hash数据结构,只需将一个hash结构bless到类上便可。它表示这个hash数据结构附加在类上,bless返回一个引用,这个引用就是对象,因此这个对象指向这个数据结构,从而对象拥有这个数据结构。
例如,绑定一个空的hash结构:
bless {},$class;
上面的bless将一个匿名hash附加到类中。
对于父类Animal来讲,因为已经有了name的属性,如今若是想要加上一个color属性,就能够将这两个成员属性放进一个hash结构中:
lib/Animal.pm中:
#!/usr/bin/env perl use strict; use warnings; package Animal; sub new { my $class = shift; my $name = shift; my $self = { Name => $name, Color => $class->default_color(), }; bless $self,$class; } sub name { my $self = shift; ref $self ? $self->{Name} # 此处须要修改,由于$self再也不是标量引用变量,而是hash引用变量 : "an unamed Class $self"; } sub default_color { die "You have to override default_color method in subclasses"; } sub speak { my $class = shift; print $class->name()," goes ",$class->sound(),"!\n"; } sub sound { die 'You have to define sound() in a subclass'; } 1;
上面将一个包含key:Name和Color的hash数据结构bless到类上,其中Name成员变量经过构造对象时传递参数赋值,Color则调用各种本身的默认颜色方法default_color(),各个子类必须重写该方法。这是显然的,咱们能够为某一子类动物设置默认毛色,但不能为全部动物设置同一种默认毛色。
而后修改lib/Horse.pm和lib/Sheep.pm,重写default_color():
lib/Horse.pm中:
#!/usr/bin/env perl use strict; use warnings; package Horse; use parent qw(Animal); sub sound { 'neigh' } sub default_color { 'black' } 1;
lib/Sheep.pm中:
#!/usr/bin/env perl use strict; use warnings; package Sheep; use parent qw(Animal); sub sound { 'baaaah' } sub default_color { 'white' } 1;
而后,speak.pl中构造Horse对象和Sheep对象,并访问本身的成员属性:
my $bm_horse = Horse->new("baima"); my $by_sheep = Sheep->new("xiaoyang"); print $bm_horse->{Name},"\n"; print $bm_horse->{Color},"\n"; print $by_sheep->{Name},"\n"; print $by_sheep->{Color},"\n";
结果:
baima black xiaoyang white
从父类中继承构造方法时,建立的对象的数据结构是彻底一致的。若是某个子类想要多添加一些固定的数据元素,可让子类重写父类的构造方法。
但须要注意的是,重写方法时,通常都强烈建议只对父类方法进行扩展,而不该该否认父类方法,彻底修改父类方法(抽象方法除外)。
例如,如今父类Animal中的构造方法以下:
sub new { my $class = shift; my $name = shift; my $self = { Name => $name, Color => $class->default_color(), }; bless $self,$class; }
想要为子类Horse添加一种固定的属性,马的类型是战马、比赛用的马仍是普通的马。因而,在Horse类中:
package Horse; use parent qw(Animal); sub new { my $self = shift->SUPER::new(@_); $self->{Type} = "Racehorse"; $self; }
注意,上面Horse中的构造方法new()中并无给bless语句。当调用Horse->new()
构建对象的时候,首先调用父类的new(),父类的new会关联一个hash结构并返回这个hash结构,这个hash结构又赋值给$self
,为此hash结构添加一种元素后,子类的new()返回$self
,使得这个hash结构成为子类对象的数据结构。
为了后面的实验,本节所修改的Horse内容请删除。
上面设置Color的时候只能经过方法default_color()设置默认的毛色,但马有黑马、棕色马、条纹马等等,因此须要能手动设置各类颜色。此外,还要更及时获取到当前最新的成员变量值,好比获取某Horse对象的名称和颜色。这就是俗称的setter和getter方法的做用。
在此示例中,Name属性是直接经过构造方法传值设置的,在逻辑上它惟一标识这个对象(对咱们而言,对perl而言是经过对象引用来惟一识别的),因此Name属性不该该容许从新设置。再者,由于设置和获取各对象的属性的代码是共性的,因此直接将这两类方法写到父类Animal中。
lib/Animal.pm中新加的代码片断:
sub set_color { my $self = shift; $self->{Color} = shift; } sub get_color { my $self = shift; $self->{Color}; } sub get_name { shift->{Name}; }
如今能够为每一个Horse或Sheep对象都设置对象本身的颜色,而且能获取颜色和名称:
my $bm_horse = Horse->new("baima"); my $by_sheep = Sheep->new("xiaoyang"); $bm_horse->set_color("white-and-black"); print $bm_horse->get_color(),"\n"; print $by_sheep->get_name(),"\n";
结果以下:
white-and-black xiaoyang
注意上面get_name()中的一种简写方式:shift->{NAME}
,shift没有给参数,因此它的操做对象是@_
,它等价于(shift @_)->{Name}
,也等价于:
my $self = shift; $self->{Name};
在为setter方法进行编码的时候,须要考虑它的返回值,通常来讲有如下4种返回值类型:
这4种返回值各有优缺点,但不管如何都请注释好返回值的类型,而且设计好以后就别再修改。
第(1)种是最通用、最多见也最简单的行为,传递什么参数给setter,就返回什么参数值,正如set_color()同样:
sub set_color { my $self = shift; $self->{Color} = shift; }
通常来讲,这种setter方法是放在空上下文(void context)中执行的,但在perl中也能够直接输出它:print set_color("COLOR")
。
第(2)种要返回设置以前的值,也很简单,只需使用一个临时变量存储一下原始值并返回该变量便可:
sub set_color { my $self = shift; my $temp = $self->{Color}; $self->{Color} = shift; $temp; }
这里有一点点小优化。由于是set,因此它多是在空上下文中执行的,也就是说这时返回以前的值是多余的。能够经过wantarray
来判断一下,wantarray函数用于检查执行上下文,若是在列表上下文中则返回true,标量上下文中则返回false,空上下文中则返回undef。
sub set_color { my $self = shift; if(defined wantarray){ # 非空上下文,返回值有用 my $temp = $self->{Color}; $self->{Color} = shift; $temp; } else { # 空上下文,无需返回值 $self->{Color} = shift; } }
第(3)种返回对象自身:
sub set_color { my $self = shift; $self->{Color} = shift; $self; }
通常来讲不会用到这种状况。但有时候有奇效,例如能够造成对象链。例如,Person有4个成员变量:Name,Age,Height,Weight,它们的setter方法都返回对象自身,那么能够:
my $people = Person->set_name("abc")->set_age(23)->set_height(168)->set_weigth(60); # 格式化一下: my $people = Person->set_name("abc") ->set_age(23) ->set_height(168) ->set_weigth(60);
第(4)种返回布尔值有时候很是有效,特别是对于常常更新出错的状况。若是是前3种返回值方式,会抛出异常,须要判断并使用die进行终止。
在面向对象编程中,常使用一个术语don't look inside box
来表示不要暴露对象的成员数据。
经过$obj_ref->{KEY}
的方式能够在类的外部访问或设置类的数据结构(成员变量),这是违反对象封装原则的,它将每一个对象的内部属性都暴露出来了。对象就像是一个黑盒子,$obj_ref->{KEY}
就像是将锁链撬开同样。
面向对象的目的之一是让Animal或Horse的维护者能够对它们的方法能独立地作出合理的修改,而且修改后那些已经导出的接口仍然可以正常工做。为何直接访问hash结构违反了这个原则?当Animal的Color属性再也不使用颜色的名称做为它的值时,而是使用RGB三原色的方式来存储颜色呢?
在此示例中,以一个虚构的模块Color::Conversions
来修改颜色数据的格式,该模块有两个函数rgb_to_name()和name_to_rgb(),用于转换RGB和颜色的字符串名称,其中name_to_rgb()返回的是一个包含RGB三原色的数组引用。
能够修改set_color()和get_color()方法:
use Color::Conversions qw(rgb_to_name name_to_rgb); sub set_color { my $self = shift; my $color_name = shift; $self->{Color} = name_to_rgb($color_name); } sub get_color { my $self = shift; rgb_to_name($self->{Color}); }
如今咱们能够照旧使用setter和getter,但内部其实已经改变了,这些改变对使用者来讲是透明的。此外,咱们还能够添加额外的接口,使得咱们能够直接设置RGB格式的颜色:
sub set_color_rgb { my $self = shift; $self->{Color} = [@_]; } sub get_color_rgb { my $self = shift; @{ $self->{Color} }; }
若是咱们在类的外面直接使用$bm_horse->{Color}
,将没法直接查看,由于它是一个RGB三原色元素列表的引用,而非直接显示出来的RGB元素值或颜色名称。
这正是面向对象编程所鼓励的行为,对于perl而言,只需将成员变量对应的值设置为一个引用便可。以关联hash数据结构的Animal类为例:
Animal |--------------------------| |-> KEY1 => $ref_value1 | |-> KEY2 => $ref_value2 | |-> KEY2 => $ref_value2 | |--------------------------|
为了让数据经过引用的方式隐藏起来,且能经过getter方法查找出来,须要合理设计setter和getter方法。例如,让setter以普通的字符串为参数,但却将其存储到一个引用中,让getter以引用为参数,但却返回人眼可识别的内容。
对于面向对象来讲,这两个方法写的实在太频繁了。perl一切从简的原则,天然也要将其简化书写:
sub get_color { $_[0]->{Color} } sub set_color { $_[0]->{Color} = $_[1] }
或者:
sub get_color { shift->{Color} } sub set_color { pop->{Color} = pop }
若是不考虑默认传递的类名或对象名参数,getter方法一般是不含参数的,setter方法一般是包含参数的。经过这个特性,能够将getter和setter合并起来:
sub get_set_color { my $self = shift; if(@_) { # 有参数,说明是setter $self->{Color} = shift; } else { # 没有参数,说明是getter $self->{Color}; } }
或者简写的:
sub get_set_color { @_ > 1 ? pop->{Color} = pop : shift->{Color} }
使用时:
# 设置颜色 $bm_horse->get_set_color("blue"); # 获取颜色 print $bm_hore->get_set_color(),"\n";
在perl中全部的方法都是子程序,没有额外的功能来区分一个方法是类方法仍是实例方法。对咱们来讲,若是传递的第一个参数为类名的是类方法,若是传递的第一个参数为对象引用名的方法是实例方法。
好在perl提供了ref函数,能够经过检查引用的方式来检查这是类方法仍是实例方法。
use Carp qw(croak); sub instance_method { ref(my $self = shift) or croak "this is a instance method"; ...CODE... } sub class_method { ref(my $class = shift) and croak "this is a Class method"; ...CODE... }
这里使用croak替换了die,这样报错的时候能够直接告诉错误所在的行数。
私有方法是指类中不该该被外界访问的方法,它能够在类自身其它地方调用,但不该该被对象或其它外界访问以避免破坏数据。
例如,类Class1中的方法get_total()
内部调用一个私有方法_get_nums()
,经过该私有方法返回的数组来获取一个包含数值的数组。若是有一个子类Class2继承了Class1,且重写了_get_nums()
使其返回一个数组引用而非数组,这时对象要调用的get_total()
整段代码就废了。
package Class1; sub new { my ($class,$args) = @_; return bless $args,$class; } sub get_total { my $self = shift; my @nums = $self->_pri_sub; # 期待该私有方法返回一个列表 my $total = 0; foreach (@nums) { $total += $_; } return $total; } sub _pri_sub { my $self = shift; ...some codes... return @nums; # 返回一个数组 } 1;
通常来讲,这样的问题并不常发生,由于程序毕竟是程序员写的,遵循规范的状况下,你们都知道这是什么意思。但若是程序比较庞大,也许无心中就重写了一个私有方法。面向对象,一个最基本的规则就是保护数据不被泄漏、不被破坏。
但perl中全部的方法都是public(公共的),谁都能访问,并无提供让方法私有化的功能。只是以一种呼吁式的规范,让你们约定俗成地使用下划线"_"做为方法名的前缀来表示这是一个私有方法(例如sub _name {}
)。但这只是一种无声的声明"这是私有方法,外界请别访问",perl并不限制咱们从外界去访问下划线开头的方法。
要实现方法的真正私有化,能够将匿名子程序赋值给一个变量来实现,或者经过闭包的方式实现。
sub get_total { my $self = shift; my @nums = $self->$_pri_sub(ARGS); my $total = 0; foreach (@nums) { $total += $_; } return $total; } my $_pri_sub = sub { my $self = shift; ...some codes... return @nums; }
须要注意的是,上面$self->$_pri_sub()
中箭头的右边是一个变量,在其它面向对象语言中是没法将变量做为方法名的,但perl支持。
由于$_pri_sub
是词法变量,构造的对象没法取得这样的数据。但经过一些高级技术,对象仍是可以取得这个方法,要想彻底私有化,经过闭包实现:
sub get_total { my $self = shift; my $_pri_sub = sub { my $self = shift; ...some codes... return @nums; } my @nums = $self->$_pri_sub(ARGS); my $total = 0; foreach (@nums) { $total += $_; } return $total; }
UNIVERSAL是一切类的祖先,全部的类都继承于它。它提供了3个方法:isa()、cat()和VERSION(),在v5.10.1和以后,还提供了另外一个方法DOES()。
1.isa()用于判断某个给定的对象(或类)与某个类是否知足is a
的关系,也就是"对象是不是某个类的实例,类1是不是类2的子类"。
$object_or_class->isa(CLASS);
2.can()用于判断某个给定的对象(或类)是否可以使用某方法。
$object_or_class->can($method_name);
需注意,can()方法检测结果为真的时候,其返回值是该方法的引用。也就是说,能够直接将can()赋值给一个方法的引用变量,避免屡次书写。如下是等价的写法:
if(my $method = $obj->can($method_name)){ $obj->$method; } if($obj->can($method_name)){ $obj->$methdo_name; }
3.VERSION()用于返回对象(或类)的版本号。
设置了our $VERSION=...;
以后,既能够经过继承自UNIVERSAL的VERSION()方法获取版本号,也能够经过对象获取VERSION变量值:
$obj->VERSION(); $obj->VERSION;
Perl支持多重继承。假设Class3多重继承Class1和Class2:
package Class3; # 1. use base qw(Class1 Class2); # 2. use parent qw(Class1 Class2); # 3. use Class1; use Class2; our @ISA = qw(Class1 Class2);
但不管是哪一种语言,都强烈建议不要使用多重继承。
假设Class1和Class2都直接继承自UNIVERSAL类,如今Class3多重继承Class1和Class2。
UNIVERSAL | | Class1 Class2 | | Class3
假设Class2有方法eat(),Class1没有,且Class3没有写eat(),那么Class3的实例调用eat()方法的时候,会调用到Class2的eat()吗?
默认状况下,Perl搜索方法的规则是从左搜索,深度优先。意味着use base qw(Class1 Class2)
时,当Class3自身找不到eat()时,将先搜索左边的Class1,搜索完没发现eat(),将搜索Class1的父类UNIVERSAL。也就是说永远也不会去搜索Class2。
实际上,搜索父类时是搜索@ISA
中的元素,因此是从左开始搜索。
可是可使用CPAN上的C3或者mro模块,它们实现从左搜索,广度优先的搜索规则。也就是说,对于use base qw(Class1 Class2)
,当Class3自身找不到eat()时,将先找左边的Class1的eat(),找不到再找右边的Class2的eat(),还找不到的话最后找父类的eat()。