Perl的面向对象
Perl不是面向对象编程的最佳选择,Perl的面向对象机制是后来嫁接进去的,下面我们就看看是怎么回事。
对象只是一个引用(也就是一个scalar变量),它恰好知道自己属于哪个类。要告诉一个引用它所指向的内容属于哪个类,使用
[bless](http://perldoc.perl.org/functions/bless.html)
。要知道引用所指向的内容属于哪个类(如果有的话),使用[ref](http://perldoc.perl.org/functions/ref.html)
。方法只是一个子程序,接受对象(或者对于类的方法,就是包名)作为第一个参数。使用
$obj->method()
可以调用对象的方法,用Package::Name->method()
可以调用类的方法。(译者注:所谓类的方法,在其他语言里就相当于类的静态方法。)类就是包含一组方法的包。
下面有个简短的例子来帮助我们弄清楚这些概念。示例模块Animal.pm
包含Animal
类,内容如下:
use strict;
use warnings;
package Animal;
sub eat {
# 第一个参数总是操作所基于的对象
my $self = shift @_;
foreach my $food ( @_ ) {
if($self->can_eat($food)) {
print "Eating ", $food;
} else {
print "Can't eat ", $food;
}
}
}
# 就这个参数来说,假设动物可以吃任何东西
sub can_eat {
return 1;
}
return 1;
然后我们可以这样使用这个类:
require Animal;
my $animal = {
"legs" => 4,
"colour" => "brown",
}; # $animal是一个普通的hash的引用
print ref $animal; # "HASH"
bless $animal, "Animal"; # 现在它是"Animal"类的对象
print ref $animal; # "Animal"
注意:任何引用都可以被转换(bless)成任何类的对象。需要由你来保证(1)这个引用指向的内容可以被当做这个类的对象来使用,并且(2)被转换成的这个类存在,并且已经被加载了。
你仍然可以按以前的方式操作这个hash:
print "Animal has ", $animal->{"legs"}, " leg(s)";
但你也可以同样用->
运算符调用这个对象的方法,就像这样:
$animal->eat("insects", "curry", "eucalyptus");
最后那句调用等价于Animal::eat($animal, "insects", "curry", "eucalyptus")
。
构造函数
构造函数是这个类返回新对象的方法。如果你需要,声明一个就是了,用你喜欢的任何名字都可以。对于类的方法,第一个参数是类名而不是一个对象,在这个例子里就是"Animal"
:
use strict;
use warnings;
package Animal;
sub new {
my $class = shift @_;
return bless { "legs" => 4, "colour" => "brown" }, $class;
}
# ...etc.
然后像下面这样使用:
my $animal = Animal->new();
继承
要创建一个类继承自基类,用use parent
,假设我们给Animal
创建一个子类叫Koala
,位于Koala.pm
:
use strict;
use warnings;
package Koala;
# 继承自Animal
use parent ("Animal");
# 重载一个方法
sub can_eat {
my $self = shift @_; # 没有使用,你也可以直接在这里写"shift @_;"
my $food = shift @_;
return $food eq "eucalyptus";
}
return 1;
下面是一些示例程序:
use strict;
use warnings;
require Koala;
my $koala = Koala->new();
$koala->eat("insects", "curry", "eucalyptus"); # 只吃eucalyptus
最后那个方法调用尝试执行Koala::eat($koala, "insects", "curry", "eucalyptus")
,但子程序eat()
并没有在Koala
包里定义。然而,因为Koala
有父类Animal
,Perl解释器会再尝试调用Animal::eat($koala, "insects", "curry", "eucalyptus")
,这回没问题。请注意Animal
类是如何自动被Koala.pm
加载的。
因为use parent
接受一组父类的名字,所以Perl支持多重继承,当然也就包含了它所带来的所有好处和噩梦。