Perlのオブジェクト指向

2020/03/04更新

目次

基本事項

Perlにおけるオブジェクト指向は、後方互換性を維持しながら後付けで考えられたものであるため、最初からオブジェクト指向で設計された他の言語と比較すると少々風変わりである。ここでは、Perlにおけるオブジェクト指向についてごく簡単に説明する。

まず基本事項として以下を理解する必要がある。

最も簡単なクラス

以下の内容の「MyClass.pm」というファイルを作る。

package MyClass;
use strict;

# コンストラクタ
sub new {
    my $class = shift;
    my @args = @_;
    # 連想配列でオブジェクトのプロパティを設定
    my $self = {
        my_prop_1 => $args[0],
        my_prop_2 => $args[1]
    };
    # 連想配列のリファレンスにbless関数を作用させて返す
    return bless($self, $class);
}

1; # 読み込み元に真値を返すために、MyClass.pm内の一番最後に常に必要

これを同じディレクトリにある別のスクリプトから以下のようにして使うことができる。

# MyClassクラスの読み込み
use MyClass;

# インスタンス生成
my $myobj = MyClass->new('AAA', 'BBB');

# コンストラクタが設定したプロパティにアクセスできる
print($myobj->{my_prop_1} . "\n");  # AAA
print($myobj->{my_prop_2} . "\n");  # BBB

もし上記でMyClassが読み込めない場合は、カレントディレクトリにモジュールパスが通っていないので、実行時に-Iオプションを使って、perl -I. スクリプトファイル名とするか、もしくはスクリプトの先頭にuse lib '.';を追加して、パスを通せばよい。特に、Perl 5.26以降では、デフォルトでカレントディレクトリが検索対象外とされているので、自分でパスを通す必要がある。

メソッド

上記の「MyClass.pm」に以下のように追記する。

# クラスメソッド
sub my_class_method {
    my $class = shift;
    my @args = @_;
    return 'XXX' . $args[0];
}

# オブジェクトメソッド
sub my_obj_method {
    my $self = shift;
    my @args = @_;
    # 自身の持つプロパティにアクセスできる
    my $prop = $self->{my_prop_1};
    return $prop . $args[0];
}

すると、以下のように呼び出せる。

print(MyClass->my_class_method('DDD') . "\n");  # XXXDDD
print($myobj->my_obj_method('EEE') . "\n");     # AAAEEE

記法のしくみ

なぜ上記のような方法が成り立つのか。実は、パッケージやbless関数を作用させたリファレンスに対してこのような記法:

MyClass->my_class_method($a, $b);
$myobj->my_obj_method($a, $b);

を使うと、実際にはそれぞれ以下をやっているのと同じことになる。

MyClass::my_class_method(MyClass, $a, $b);
MyClass::my_obj_method($myobj, $a, $b);

このように、パッケージ内のサブルーチンの第1引数に自分自身を渡して呼び出しているだけなのである。メソッドの1行目に必ずmy $class = shift;my $self = shift;が登場するのはこのためである。

また、以下のような記法もしばしば見かける。

new MyClass($a, $b);

これは間接オブジェクト記法と呼ばれ、以下と同じである。

MyClass->new($a, $b);

このようにnewは特別な語句ではなく、単なる1つのメソッドの名前に過ぎない。

間接オブジェクト記法は、他にもファイルへ出力する際にprint $fh "~";と書くときにも使われるが、これも実際は$fh->print("~");という意味である。print文での使用は見慣れたものとなってしまっているが、それ以外では混乱の元となるので、多用しない方が良い。new MyClassよりもMyClass->newの方が良いとされている。