Blog::kobaken

prove t/foo/bar/baz.t

perlのblessは何か?

blessは、任意のリファレンスをパッケージに結びつける関数です。 パッケージと結びつけられたリファレンスをオブジェクトと呼びます。

次のコードは、ハッシュリファレンスの$pointとSome::Pointパッケージを結びつけています。 他の言い方をすれば、ハッシュリファレンスの$pointをSome::Pointパッケージがblessしています。

my $point = { x => 3, y => 4 };
bless $point, 'Some::Point';

package Some::Point {
  sub radius {
    my $point = shift;
    return sqrt($point->{x} ** 2 + $point->{y}**2)
  }
}

リファレンスとパッケージを結びつけると、関数の呼び出しに関して特別対応されます。

例を用いて、順に説明します。 上記のコードの通り、Some::Pointパッケージには、三平方の定理で半径を求めるradius関数が定義されています。 このradius関数は、Some::Point::radius(..)のようにパッケージ名::関数名(..)と書けば、呼び出せます。

my $point = { x => 5, y => 12 };
Some::Point::radius($point) # => 13

そして、肝心の特別対応ですが、ハッシュリファレンスの$pointを、Some::Pointパッケージでblessした時、 $point->radius()と書けば、Some::Point::radius($object)と同等の呼び出しになります。

my $point = { x => 5, y => 12 };
bless $point, 'Some::Point';

$point->radius(); # Some::Point::radius($object) と同等

つまり、blessすることで、オブジェクトに紐づいたパッケージの関数呼び出しができます。この呼び出し方をメソッド呼び出しと言います。

まとめ

blessには、任意のリファレンスをパッケージに結びつけて、メソッド呼び出し構文を使えるようにする効果があります。

おまけ

補足1:

$point->some_method のように、Some::Pointパッケージに定義されていない関数が指定された場合、親のパッケージにその関数がないか探す効果もあります。 bless関数の説明しなかった効果は、これくらいです。

補足2:

結びつけられるリファレンスは、どんなリファレンスでも良いです。 ハッシュリファレンスがよく使われますが、配列リファレンス、スカラリファレンス、コードリファレンスが使えます。 例えば、このSome::Pointを、次のように書き換えたら、メモリ効率、パフォーマンスの利点があるでしょう。

my $point = [3,4];
bless $point, 'Some::Point';

package Some::Point {
  sub radius {
    my $point = shift;
    return sqrt($point->[0] ** 2 + $point->[1]**2)
  }
}

補足3:

慣習的に、blessする関数の名前をnewと名付けることが多いです。

package Some::Point {
    sub new {
        my ($class, $x, $y) = @_;
        return bless [$x, $y] => $class;
    }

    sub radius {
        my ($self) = @_;
        return sqrt($self->[0] ** 2 + $self->[1]**2);
    }
}

my $point = Some::Point->new(3,4);
$point->radius; # => 5

補足4:

ざっくりした咀嚼をすると、「パッケージにはふるまいが定義され、リファレンスは状態を持ち、 blessすると、ふるまいと状態を結びつけたオブジェクトができる」と考えられます。

補足5:

v5.38からperlのコアにクラスを作成するためのclass featureが追加されました。より現代的なオブジェクト指向プログラミングをする場合、こちらを利用する方が良いと思います。もし、5.38が利用できない場合は、v5.14以上であれば、Feature::Compat::Classを利用して同等の機能を利用できます。

SEE ALSO

https://perldoc.jp/func/bless