05 月 04 日(Tue)のアレゲメモ
Perl におけるオブジェクト指向
Perl のオブジェクト指向プログラミングは、 Java や C++ のようなクラスベースではありません。 また、JavaScript のようなプロトタイプベースでもありません。 なので、ほかの言語になれている人が Perl でオブジェクト指向プログラミングをやろうとすると、 いろいろと違和感を覚えてしまうと思うので、 その変わったところをまとめてみます。
bless とオブジェクト
Perl のオブジェクトを作るときは、 まず組み込みのデータ型から適当なものを選びます。 普通はハッシュを使うことが多いと思いますが、 配列やクロージャ(無名サブルーチン)もオブジェクトに出来ます。
ハッシュを使ってオブジェクトを作るには、 まず普通にハッシュを作って:
my %self = (foo => 1, bar => 2);
次のそのリファレンスをクラス名に「bless」します:
my $object = bless \%self => "MyClass";
この bless の戻り値がオブジェクトになります。
もちろん、\%self の変わりに直接ハッシュリテラルのリファレンスを書いてもかまいません:
my $imm_object = bless {foo => 1, bar => 2} => "MyClass";
つまり、Perl のオブジェクトとは、 組み込み型のデータにクラス名を付加したものでしかないのです。
クラスは型ではない
Perl にはデータ型としてのクラスはありません。 Perl のクラスは、実際のところはパッケージ、すなわち名前空間です。
従って、同じクラス名で異なるデータ型のリファレンスを bless してもかまいません。
my $hashobj = bless {foo => 1, bar => 2} => "MyClass"; # ハッシュ
my $arrobj = bless ["foo", "bar"] => "MyClass"; # 配列
同じクラスなのにデータ型が違うものが許されるとなると、 そもそもクラスを作ることに意味はあるのでしょうか?
メソッド呼び出し
パッケージ
上述のように、Perl のオブジェクトは同一のクラスであってもデータ型を共有しません。 その代わり、 メソッドを探す名前空間を共有します。 この名前空間を決めるのがパッケージです。
クラスのメソッドを作るには、まず package で名前空間を指定します:
package MyClass;
それに続いてサブルーチンを定義すると、 それらはそのクラスのメソッドとなります。
sub hello {print "HELLO!\n"}
メソッドを呼び出すときは、オブジェクトに「->」を続けてメソッド名を指定します:
$object->hello;
あるいは、メソッド名とオブジェクトを空白でつなげる書き方もできます:
hello $object;
これだと、「->」という変な記号もなくなるし、 なんとなく英語の文章のようになります。
インボカント
オブジェクトのメソッドが呼び出されたとき、 暗黙のうちにそのオブジェクトそのものが、 サブルーチンの第 1 引数として呼ばれます。
つまり、次の 2 つの文はまったく同じです:
$object->hello; MyClass::hello ($object);
この暗黙の第 1 引数のことをインボカント(invocant)といいます。
なので、サブルーチンを書くときはこのことに注意して、 第 1 引数をきちんと受け取るようにしましょう。 たとえば、次のように書くことができます:
sub hello {
my $self = shift;
print "HELLO! I am $self.\n";
}
ここで、このメソッドを呼び出すと、 だいたい次のような出力になるでしょう:
HELLO! I am MyClass=HASH(0x8129790).
これは、第 1 引数として、 「MyClass」というパッケージで bless されたハッシュリファレンスが渡されたことを示しています。
メソッド引数
インボカントのことを忘れなければ、 後は通常のサブルーチンと同じように引数を渡すことができます:
sub set_name {
my $self = shift;
my ($name) = @_;
$self->{name} = $name;
}
$object->set_name ("Haruko");
ここでは、インボカントは shift で取って、 それ以外の引数は my 変数にコピーするようにしていますが、 もちろん次のように書いてもかまいません:
my ($self, $name) = @_;
メソッドの冒頭で引数を shift してインボカントを取得しておくことで、 メソッド本体の中ではいつでも「@_」の形で引数リストを参照できるので、 個人的には上の例のような書き方をよくします。
なお、上の例ではインボカントがハッシュリファレンスであることを想定しているので、 ハッシュ以外で作ったオブジェクトに対して呼ばれた場合は、 もちろんエラーになります。
$arrobj->set_name ("Harumi"); # エラー
継承
(まだ)