Perl は以前からオブジェクト指向プログラミングをサポートしてきました。
しかし、Perl そのものはオブジェクト指向プログラミング言語ではないため、
オブジェクト指向プログラミングの構文は用意されておらず、他の言語と比べて独特なコーディングを強いられてきました。
ところが、Perl v5.38.0 に実験的に class
構文が組み込まれました。
これにより他のプログラミング言語とよく似た形でオブジェクト指向プログラミングができるようになります。
この記事では、Perl v5.38.0 の実験的な class
構文について、Perl ドキュメントの
perlclass
を参考に紹介します。
Perl の class 構文は Perl v5.38.0 以降でないと利用できません。
恐らく多くの環境では Perl v5.38.0 はインストールされていないことでしょう。
また、class
構文はまだ実験的な実装のため、
class
構文を試すためだけにインストール済みの Perl をバージョンアップするのはためらわれます。
そのため、ここでは、ホームディレクトリ内に Perl 本体をインストールして、一時的にそれを使う方法を紹介します。
この記事を書いている時点では、残念ながら Windows 用の Strawberry Perl も Active Perl も Perl v5.38.0 は用意されていませんでした。 また、Linux の各種ディストリビューションでも、標準で Perl v5.38.0 は用意されていませんでした。 そのため、ここでは Ubuntu にソースコードから make してインストールします。 一般ユーザー権限で、ホームディレクトリに Perl 本体をインストールします。 CPAN のソースコード配布ページ に掲載されているインストール手順を以下に引用します。
wget https://www.cpan.org/src/5.0/perl-5.38.0.tar.gz
tar -xzf perl-5.38.0.tar.gz
cd perl-5.38.0
./Configure -des -Dprefix=$HOME/localperl
make
make test
make install
make install
が終わったら、シェルで次のコマンドを実行します。
export PATH=$HOME/localperl/bin:$PATH
perl
を呼び出したときのバージョンを確かめてみます。
$ perl -v
This is perl 5, version 38, subversion 0 (v5.38.0) built for x86_64-linux
[以下省略]
これで、シェルを閉じるまで、perl
コマンドで Perl v5.38.0 が呼び出されるはずです。
まずは、class
構文のサンプルコードを見てみましょう
use v5.38;
use feature 'class';
class My::Example 1.234 {
field $x;
ADJUST {
$x = "Hello, world";
}
method print_message {
say $x;
}
}
My::Example->new->print_message;
このコードを見れば、Perl 経験者でなくても、おおよそ何をやっているのか分かるのではないでしょうか。
My::Example
という名前のクラスが定義されています。
そのクラスにはプロパティ x
と print_message
メソッドが用意されています。
さらに、ADJUST
のブロックではプロパティ x
が文字列
"Hello, world"
で初期化されています。
最後の行では、このクラスのインスタンスを生成して print_message
メソッドが実行されています。
当然、この結果として "Hello, world"
と出力されるはずです。
なお、現時点では、class
構文を使いたなら、use v5.38;
と
use feature 'class';
の 2 つの宣言は必須です。
実際に上記スクリプトを実行すると、次のような結果が出力されます。
class is experimental at ./test.pl line 6.
field is experimental at ./test.pl line 7.
ADJUST is experimental at ./test.pl line 9.
method is experimental at ./test.pl line 13.
Hello, world
この class
構文はまだ実験的な実装のため、このように警告がいくつも出力されます。
以降、class
構文を詳細に見ていきましょう。
クラスの宣言には class
キーワードを使います。
class
キーワードの後ろに半角スペースを挟んでクラス名を指定します。
次の例は Example
という名前のクラスを定義しています。
class Example {...}
次のようにクラス名には名前空間を加えることも可能です。
次の例では My
を名前空間として追加しています。
class My::Example {...}
さらに、class
気ワードにはクラスのバージョンも加えることが可能です。
次の例ではバージョン番号 1.000
を指定しています。
class My::Example 1.000 {...}
フィールドとは、クラスの中からしかアクセスできない変数を指します。 このフィールドは、クラスインスタンスごとに記憶領域が用意され、インスタンスごとに独立しています。 オブジェクト指向プログラミングでは、よくメンバー変数とも呼ばれます。
Perl の class
構文では、フィールドはクラス定義の中で field
キーワードを使って定義します。
class Member {
field $userid = 'Taro';
field @interests = ( 'car', 'game', 'tech' );
field %scores = ( 'A' => 80, 'B' => 40, 'C' => 90 );
method print_message {
say join( ', ', $userid, $interests[2], $scores{B} );
}
}
Member->new->print_message;
上記コードのようにフィールドとしてスカラー変数だけでなく配列や連想配列も定義することができます。 もちろん、HASHREF や ARRAYREF などのリファレンスにすることも可能です。
class Member {
field $userid = 'Taro';
field $interests = [ 'car', 'game', 'tech' ];
field $scores = { 'A' => 80, 'B' => 40, 'C' => 90 };
method print_message {
say join( ', ', $userid, $interests->[2], $scores->{B} );
}
}
Member->new->print_message;
ADJUST
ブロックを使って、クラスインスタンス生成時の初期化処理を定義することができます。
class Member {
field $userid = 'Taro';
field $len;
ADJUST {
$len = length $userid;
}
method show_userid {
say "${userid} (${len})";
}
}
Member->new->show_userid; # Taro (4)
メソッドは method
キーワードを使って定義します。
method
キーワードの後ろにはメソッド名を、必要であれば引数を定義することができます。
また、引数のデフォルト値を定義することも可能です。
class Member {
field $userid = 'Taro';
method greet( $hi = 'Hi!' ) {
say $hi . ' ' . $userid;
}
}
Member->new->greet; # Hi! Taro
Member->new->greet('Hello'); # Hello Taro
クラスからインスタンスを生成するとき、つまり、new
を呼び出す際に、
コンストラクタにパラメーターを引き渡すことが可能です。
class Member {
field $userid :param;
method greet( $hi = 'Hi!' ) {
say $hi . ' ' . $userid;
}
}
my $member = Member->new('userid' => 'Taro');
$member->greet; # Hi! Taro
上記の例では、userid
という名前に対して "Taro"
という値のペアをコンストラクタに引き渡しています。
この引き渡されたパラメータは、:param
属性を使って受け取ります。
パラメータに使われた名前と同じ変数にパラメータ値がセットされることになります。
上記クラス定義において、もし new
にパラメータを指定しなかった場合は
例外が投げられます。
# パラメータを指定しなかったために例外が投げられます。
my $member = Member->new();
:param
属性にパラメータの別名を定義することもできます。
class Member {
field $userid :param(nickname);
method greet( $hi = 'Hi!' ) {
say $hi . ' ' . $userid;
}
}
my $member = Member->new(nickname => 'Taro');
$member->greet; # Hi! Taro
上の例では、本来は userid
という名前でパラメータを受け取るところを、
nickname
という名前でパラメータを受け取ります。
ただし、別名を定義すると、元の名前 userid
ではパラメータを受け取ることはできなくなりますので注意してください。
:isa 属性
を使って、別のクラスを 1 つだけ継承することができます。
# 親クラス
class Member {
field $userid :param;
method greet( $hi = 'Hi!' ) {
say $hi . ' ' . $userid;
}
}
# 子クラス
class ProMember :isa(Member) {
field $nickname :param;
method thank {
say 'Thank you, ' . $nickname;
}
}
my $promember = ProMember->new(userid => 'Taro', nickname => 'Kotaro');
$promember->greet; # Hi! Taro
$promember->thank; # Thank you! Kotaro
上の例では、親クラス Member
にはフィールド userid
とメソッド greet
が定義されています。
子クラス ProMember
にはフィールド nickname
とメソッド thank
が定義されています。
子クラスのインスタンスを生成すると、子クラスに定義されたメソッド thank
だけでなく、
親クラスに定義されたメソッド greet
も期待通りに呼び出すことができます。
コンストラクタにパラメータが引き渡されていますが、各パラメータの値は親と子のそれぞれに分配されます。 しかし、親クラスに引き渡されたパラメータ値は、子クラスから読み取ることはできません。 つまり、フィールドは継承されません。継承されるのはメソッドのみですので注意してください。
以上で Perl v5.38.0 に実験的に実装された class
構文を一通り解説しましたが、
オブジェクト指向プログラミングのすべてを網羅しているわけではありません。
現時点ではまだ実験的実装のため、今後、さまざまな機能が追加されると思われます。
この構文は、ある意味、Perl っぽくないと感じるかもしれませんが、慣れてくると、
この構文のほうが分かりやすく書きやすいと感じることでしょう。
さらなる機能追加を、今後のバージョンアップに期待したいところです。