Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

Perlで矢印演算子を使わない場合に継承がどうなるのかのメモ

こういうパッケージがあるとする.

# lib/Hoge/A.pm
package Hoge::A;

sub foo {
  return "I am A"
}

1;

でもってこれを継承したBも作る:

# lib/Hoge/B.pm
package Hoge::B;

use parent qw(Hoge::A);

sub foo {
  return "I am B"
)

1;

これだけだとただの継承だが,Aにもう1つメソッドを生やす:

package Hoge::A;

sub foo {
  return "I am A"
}

sub call_foo {
  warn foo;
}

1;

自分自身のfooを呼び出している.継承関係のおかげで,call_fooHoge::Bから使うことができる.

問題です

Hoge::B->call_fooの結果はどうなるでしょう??

# test.pl
use Hoge::A;
use Hoge::B;

Hoge::B->call_foo;

windymelt% perl -I lib ./test.pl
I am A at lib/Hoge/A.pm line 7.

Bのfooは呼ばれなかった.

理由

warn $class->fooではなくwarn fooと書いていたため.

A.pmを修正してみる.

# lib/Hoge/A.pm
package Hoge::A;

sub foo {
  return "I am A"
}

sub call_foo {
  warn foo; ## ここを
}

1;
# lib/Hoge/A.pm
package Hoge::A;

sub foo {
  return "I am A"
}

sub call_foo {
  my ($class) = @_;
  warn $class->foo; ## こうじゃ
}

1;

するとHoge::Bfooが呼ばれるようになる.

windymelt% perl -I lib ./test.pl
I am B at lib/Hoge/A.pm line 7.

解説

->オペレータがミソ.perldoc曰く:

サブルーチンがどのパッケージにあるかを Perl はどうやって知るのでしょうか? 矢印の左側を見ます; これはパッケージ名かオブジェクトへのリファレンス (つまりパッケージに bless された何か) のどちらかである必要があります。 どちらの場合も、それが Perl が探し始めるパッケージです。 もしそのパッケージに指定された名前のサブルーチンがないなら、Perl は そのパッケージの基底クラスを探し始めます; それを繰り返します。

perldoc.jp

->を使わない場合,本来行われるべきメソッドの解決が行われず,ただのサブルーチン呼び出しになるため,サブルーチンを呼び出した箇所から参照可能な定義が勝手に選ばれてしまい,継承先でおかしな挙動を招いてしまう. ->を使うことで初めて継承関係が解決されて実際にどの定義が呼び出されるべきかが決定される,と考えよう.

そもそも->を使わない場合,もはやそれはメソッドの要件を満たしていないということになる.クラスのパッケージを第一引数に受け取るのがメソッドである.なので,引数が無いメソッドは->を使わずに呼べるがこのような形を取るべきではない($classだけ引数に取るべきだ).

->オペレータは,Perlのオブジェクト指向らしさを一手に担っていると考えることもできる.よくできてるね.

まとめ

  • ->は継承を行うが,そうでないサブルーチン呼び出しは継承を行わない
  • オブジェクト指向するなら->を使うこと
★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?