メソッド (計算機科学)
出典: フリー百科事典『ウィキペディア(Wikipedia)』
メソッド (method) あるいは メンバ関数 (-かんすう, member function) とはオブジェクト指向プログラミング言語において、あるクラスないしオブジェクトに所属するサブルーチンを指す。
目次 |
[編集] 概要
C++ などの一部のプログラミング言語では、メソッドはメンバ関数と呼称される。「メンバ」関数という限定的な呼び名からも明らかなように、メンバでない関数の存在を言外に示しており、そのため Java などそのような関数が存在しない (全てのサブルーチンが何らかのクラスに所属する) 言語ではメソッドという用語を用いることが多い。また Objective-C のように関数が存在する言語でも、機能を直接指定する関数と動的ディスパッチを行うメソッドの差異から「メンバ関数」という用語が適当でない場合がある。しかしいずれにせよメソッドとメンバ関数の間に概念上明確な差はない。
メソッドと通常の関数の違いは主にインスタンス内部へのアクセスの有無である。この機能は通常カプセル化と呼ばれており、クラス定義の抽象化に貢献するとされる。また呼び出し時に操作の対象となるインスタンス(レシーバ) を self
や this
といった予約語、あるいはメソッドに渡された引数によって参照することができる。 C++ ではクラスに属さない関数であるグローバル関数と対比されることがある。
またメンバ関数はサブクラス化の際にオーバーライドされる可能性があり (多態性)、実際に発生する動作がレシーバに依存するという特徴を持つ。
UML ではメソッドのことを操作と呼ぶ。
[編集] インスタンスメソッドと静的メソッド
インスタンスメソッド (instance method) とはインスタンスに属するメソッドのことであり、生成されたインスタンスからアクセスする。インスタンスメソッドはオブジェクト指向プログラミングの中核をなし、もっともよく使われれる。
一方、静的メソッド (static method) あるいは クラスメソッド (class method) とはクラスに属するメソッドのことであり、インスタンスを生成せずにクラスから直接呼び出すことができる。基本的に、その振る舞いは非オブジェクト指向言語における関数と変わらない。
[編集] インスタンスメソッドと静的メソッドの例
インスタンスメソッドと静的メソッドの例を以下の Java によるサンプルで示す。Java では static
修飾子がついたメソッドが静的メソッドであり、ついていなければインスタンスメソッドである。
public class MethodSample { /** インスタンスフィールド */ private String name; /** 静的フィールド */ private static int number; /** インスタンスを生成するためのコンストラクタ */ public MethodSample(final String name) { this.name = name; } /** インスタンスメソッド、getter */ public String getName() { return this.name; } /** 静的メソッド、getter */ public static int getNumber() { return MethodSample.number; } /** 静的メソッド、setter */ public static void setNumber(final int number) { MethodSample.number = number; } }
インスタンスメソッドを呼び出すには、まずコンストラクタを呼び出してインスタンスを生成しなければならない:
MethodSample obj1 = new MethodSample("John"); MethodSample obj2 = new MethodSample("Joe");
上の例では obj1
と obj2
の二つのインスタンスを生成した。この時点で、obj1
とobj2
のインスタンスフィールド name
にはそれぞれ "John" と "Joe" が代入されている。
インスタンスメソッドを呼び出すには次のように記述する:
obj1.getName() // "John" を返す obj2.getName() // "Joe" を返す
上の例では obj1
と obj2
それぞれのインスタンスに対し getName
メソッドを呼び出した。それぞれの呼び出しの返り値が異なることから、インスタンスフィールドは、そのクラスに属する各オブジェクト毎に異なる値を持つことがわかる。
一方、静的メソッドはクラスに直接属しているため、呼び出す際はコンストラクタによってインスタンスを生成せずとも、即座にメソッドを呼び出せる。静的メソッドを呼び出すには次のように記述する:
MethodSample.setNumber(100); MethodSample.getNumber(); // 100 を返す
このとき、 MethodSample
が複数のインスタンスを生成しても、静的フィールド number
はすべての MethodSample
クラスに属するインスタンスの間で共有される。
[編集] 仮想メソッド、抽象メソッドと具象メソッド
[編集] 仮想メソッド
仮想メソッド (virtual method) (C++では仮想関数)とは、サブクラスでオーバーライドし、動作を変更することのできるメソッドのことである。
C++およびC#ではメソッドにvirtual
修飾子をつけることで仮想メソッドとすることができる。一方Javaではメソッドはデフォルトで仮想メソッドとなり、final
修飾子をつけることで非仮想メソッドとなる。
[編集] Javaにおけるfinal
修飾子、非virtual
に関する都市伝説
一般に、仮想メソッドはコンパイル時にどのメソッドを呼び出すかを確定できないため、通常のメソッド呼び出しよりもパフォーマンスが悪いというデメリットがある。そのため、パフォーマンスを気にする多くのC++プログラマは、とくに継承する必要がないときはメンバ関数にvirtual
修飾子をつけることを非常に嫌がる。
その影響もあってか、Javaでも、「Javaはデフォルトで仮想的だからfinal
をつけないとパフォーマンスが衰える。」と言われるようになった。しかし、Javaコンパイラの最適化技術は大幅に進歩し、final
をつければパフォーマンスが向上するということは無くなってしまった。そのため、今日ではfinal
をつければ高速化するという話は都市伝説であると言われるようになった[1]。
[編集] 抽象メソッドと具象メソッド
抽象メソッド (abstract method) 、あるいはC++では純粋仮想関数 (pure virtual function) とは仮想メソッドの一種で、メソッドの実装が無く、宣言だけされているもののことである。このメソッドを利用するには、このメソッドを含むクラスを継承し、そこでこのメソッドをオーバーライドして実装する必要がある。従って、抽象メソッドを含むクラスは継承しない限りインスタンス化できない。このようなクラスを抽象クラスと呼ぶ。
具象メソッド (concrete method) は抽象メソッドの逆で、実装をもつメソッドのことである。主に抽象メソッドをオーバーライドしたインスタンスメソッドのことを意味するために使われる。
Javaではabstract
修飾子を用いて抽象メソッドを宣言できる。抽象メソッドを持つクラスもまた、必ずabstract
修飾子を使わなければならない。
抽象メソッドはデザインパターンの一つTemplate Method パターンで主要な役割を果たす概念であり、ソフトウェアの拡張性、再利用性、汎用性を高めるのに役立つ。
[編集] オーバーライド
詳細はオーバーライドを参照
C#では、オーバーライドしたメソッドにはoverride
修飾子をつけることで、そのメソッドがオーバーライドされていることをコンパイラに知らせることができる。 同様に、Javaでも、Java SE 5から導入されたアノテーション@Override
を用いることでメソッドがオーバーライドされていることをコンパイラに知らせることができる。
あるスーパークラスとそれを継承したサブクラスが別々の開発者によって実装されている場合、Javaではオーバーライドに関係した問題が起こりうるので注意が必要である。スーパークラスの実装者があとからメソッドを追加したときに、そのメソッドと同じ名前とシグネチャのメソッドが既にサブクラスに存在すると、オーバーライドしたつもりがないのに関係のないメソッドをオーバーライドしてしまうという問題が起こる。これを回避するためにJavaでは@Override
の指定が推奨されるが、後方互換性を保つために必須とはなっていない。C#ではoverride
修飾子が必須なのでこの問題は起こらない。
[編集] アクセサ
アクセサ (accessor) とは、メソッドの中で特にオブジェクトの属性やプロパティを取得するメソッド (getter) と設定するメソッド (setter) の対のこと。基本的に getXXX, setXXX のように語幹部が揃っているものをいう。
[編集] アクセサの利点
フィールドへの直接アクセスと比較した際のアクセサの利点は、第一に、意図的に setter メソッドを実装しないことで、フィールドの値を勝手に変更させないようにイミュータブル(不変)なクラスを作ることが可能になることが挙げられる。第二に、getter/setter でフィールドへアクセスするときにデータを加工してから取得したり挿入することができるという点である。第三に、setter でフィールドにデータをセットするときにセットしたいデータを例えば整数のみ、あるいは自然数のみ、偶数のみ、日付のみ、などに限定することができるという点である。第四に、モジュール間の依存関係を疎にしやすくなり、オブジェクト指向言語としての利点を活かせるという利点がある。
[編集] アクセサ論争
Smalltalk や Objective-Cのようなメッセージ送信メタファーの言語では、インスタンス変数は必ずアクセサを用いて利用するというのが常識である。一方 C++ などでは public
変数への直接フィールドアクセスが往々にして利用されることがある。
- 効率の問題。メソッドをいちいち呼び出すコストを避けたい場合。ただしほとんどはインライン化で解決できる。
- 記述量の問題。単純に値をセット、取得するだけのことに全てメソッドを記述するのは間違っているという考え方。
問題となるのは後者である。
まず多態性の観点から、アクセサを用いない変数参照は将来にわたっての変更耐久性が著しく劣る。また記法の一貫性からアクセサを指示する向きもある。一方否定派は「強力な IDE を用いればリファクタリングは可能であり、むしろフィールドへの直接アクセスを用いる方が意味が明確となる」という主張を展開し、時にフレームに発展する場合がある。これは現代の「goto文論争」ともいうべき、半ば宗教的な対立関係である。
C# はこの反省から、アクセサ定義を簡素化する要素としてプロパティを導入した。また Ruby など、アクセサを簡単に定義できるメソッドや構文を備えている言語もある。
[編集] オーバーロード
詳細は多重定義を参照
多くのオブジェクト指向言語ではメソッドやコンストラクタをオーバーロード (overload) できる機能を持つ。これは、オーバーライドと名前が似ているため間違えられることがある。引数の数、型、順序などが異なる同じ名前のメソッドを定義することをメソッドのオーバーロードという。
ただし、PHPやPerlのように、プログラミング言語によっては、型の曖昧さが原因によりメソッドをオーバーロードできないものもある。この場合はメソッドの引数をメソッド先頭で読み取り、引数の型を判定する条件分岐で対応する。
[編集] 参考文献・脚注
- ^ Javaの理論と実践: パフォーマンスの都市伝説 ガーベッジ・コレクターなどのプログラミングに棲みついているワニについて 都市伝説その2: クラスやメソッドをfinalとして宣言すると処理が速くなる