[???] /
[Java FAQ] / [S011]
S011: メソッド - method
[S011 Q-01]
オーバライドとは何ですか?
[S011 A-01]
スーパークラスに宣言されているメソッドと同じ名前で、同じ仮引数(型,数)を
持つメソッドをサブクラスに宣言することです。
戻り値の型が異なる場合はオーバライドできません。
以下にオーバライドの例を示します。
class Parent {
void overrideMethod() {
System.out.println("This is Message from Parent");
}
}
class Child extends Parent {
void overrideMethod() {
System.out.println("This is Message from Child");
}
}
public class Test {
public static void main(String[] args) {
Parent parent;
parent = new Parent(); // スーパークラスを作成
parent.overrideMethod(); // Parent#overrideMethod を呼び出す
parent = new Child(); // サブクラスを作成
parent.overrideMethod(); // Child#overrideMethod を呼び出す
}
}
参考記事 [JavaHouse-Brewers:7545]
[S011 Q-02]
オーバロードとは何ですか?
[S011 A-02]
1つのクラスに仮引数の型か数が異なる同名のメソッドを複数宣言することです。
オーバロードを行う側面には以下の3つがあります。
1. 複数表現できるパラメータを統一的に表したい場合
2. パラメータのセットを省略できるようにしたい場合
3. パラメータのセットによって挙動を変えたい場合
----
1. 複数表現できるパラメータを統一的に表したい場合
例えば、Component#getComponentAt() メソッドは「与えられた座標に位置するコン
ポーネントを返す」という仕様です。座標の表現として、
int x, int y という形式と、座標を表す Point オブジェクトがありますが、
それぞれをパラメータとして受け取る同じ名前のメソッドが宣言されています。
getComponentAt(int x, int y)
getComponentAt(Point p)
----
2. パラメータのセットを省略できるようにしたい場合
例えば、String#getBytes() メソッドは、「文字列をバイト配列にして返す」
という仕様です。明示的にエンコーディングを指定してバイト配列を
取得することもできますが、引数を全くとらないメソッドも同時に宣言されています。
エンコーディングを省略した場合、システムのデフォルトエンコーディングを
使用します。
getBytes(String enc)
getBytes()
----
3. パラメータのセットによって挙動を変えたい場合
例えば、javax.servlet.GenericServlet#log() メソッドは、「ログを出力する」
という仕様です。出力するメッセージだけを引数にとるメソッドと、
出力するメッセージと Throwable オブジェクトを引数にとるメソッドの2つがあります。
Throwable オブジェクトを引数にとった場合、
log(String msg)
log(String msg, Throwable t)
----
参考記事 [JavaHouse-Brewers:7545]
[S011 Q-03]
オーバライドしたはずのメソッドが呼ばれないのですが?
[S011 A-03]
メソッド名のタイプミスの可能性をまず疑って下さい。
イベントにおけるXxxxAdapterクラスの例にについて記します。
XxxxAdapter クラスを継承してイベントハンドリングメソッドをオーバー
ライドしたつもりが、スペルミスにより異なるメソッドとして定義してし
まっていたため、そのメソッドが呼ばれることがないということがありま
す。
例えば以下のメソッドは呼び出されることはありません。
frame.addWindowListener(new WindowAdapter() {
public void WindowClosing(WindowEvent ev) {
System.exit(0);
}
});
ウィンドウが閉じられようとしたときに呼び出されるのは
windowClosing(WindowEvent) メソッドであって、
WindowClosing(WindowEvent) メソッドではありません。API ドキュメン
トを見直してこのような間違いが無いか再度確認してください。
尚、interface を implements する(この場合 XxxxListener を
implements する)場合は、このようなミスがあってもコンパイル時にエラ
ーが報告されるためこの問題は発生しません。
参考記事 [JavaHouse-Brewers:22959]
[S011 Q-04]
クラスメソッドからインスタンス変数が参照できないのはなぜですか?
[S011 A-04]
インスタンス変数はインスタンスに付随する物であり、クラスメソッドは
特定のインスタンスに属さないため参照できません。
但し、以下の様に明示的にインスタンスを指定すれば参照できます。
class Test {
int fieldA;
public static void doTest() {
// fieldA = 1; ではエラー
Test Test = new Test();
test.fieldA = 1;
}
}
参考記事 [JavaHouse-Brewers:3591]
[S011 Q-05]
クラスメソッドからインスタンスメソッドを呼び出せないのはなぜですか?
[S011 A-05]
S011-04 を参照。変数でもメソッドでも同様です。
参考記事 [JavaHouse-Brewers:6706]
[S011 Q-06]
クラスメソッドはオーバライドされないのですか?
[S011 A-06]
クラスメソッドにはオーバライドという概念はありません。
オーバライドしたつもりで、同じシグネチャのメソッドを宣言し
ても、実際に呼び出されるメソッドは、オブジェクトの存在とは無関係に、
ソースコード中の該当メソッド呼び出し時に使用される「コンパイル時の型」だけ
によって決まります。
「コンパイル時の型」とは、変数にオブジェクトを代入した場合の変数の型や、
式自体の表す型(メソッドの復帰値として宣言された型やキャスト
によってソースコード上で指示された型)を表します。
いずれも実際のオブジェクトの「実行時のクラス」ではないことに注意して下さい。
以下に例を示します。
class Foo {
static int foo() {
return 1;
}
static void bar() {
System.out.println("foo() = " + foo());
}
}
class Baz extends Foo {
static int foo() {
return 2;
}
static void main(String[] args) {
bar();
}
}
出力結果:
foo() = 1;
参考記事 [JavaHouse-Brewers:15615]
[S011 Q-07]
オーバライドされたスーパークラスのメソッドを呼ぶにはどうしたら良いですか?
[S011 A-07]
super.メソッド名() とします。
参考記事 [JavaHouse-Brewers:7844]
[S011 Q-08]
メソッドをオーバライドするときthrows節の例外を増やせないのはなぜですか?
[S011 A-08]
A. 文法上許されていないためです。
もし、サブクラスがオーバライドするメソッドでthrows節に宣言する例外を
増やすことを許可してしまうと、スーパークラスの利用者側が想定しない例外が
発生することになってしまうため、安全性に問題が起ってしまいます。
逆に、スーパークラスのメソッドで宣言されている例外の一部または全部を、
サブクラスでオーバライドするメソッドで宣言しない、つまり、例外を減らす分には
単にその例外が発生しないことになるだけなので、問題は生じません。
なお、オーバライド時に、スーパークラスで宣言されている例外のかわりに、
その例外のサブクラスを宣言することはできます。これにより、サブクラスで発生する例外を
スーパークラスで宣言されている一般的な例外より特定なもののみに限定することができます。
参考記事 [JavaHouse-Brewers:12370]
[S011 Q-09]
methodA(Object arg) と methodA(String arg) があるとき、
methodA(null) と記述するとどちらが呼ばれるのですか?
[S011 A-09]
この場合は methodA(String arg) を呼び出します。
null はすべての型に適合します。String は Object を継承していますの
で、より具体的 (特殊) な型である String を引数にもつメソッドが選択
されます。継承関係のないクラス同士の場合は「参照はあいまいです」
とコンパイルエラーになります。
参考記事 [JavaHouse-Brewers:5244]
[S011 Q-10]
method(InterfaceA arg) と method(InterfaceB arg) があるとき、
あいまいだと言われるのはなぜですか?
[S011 A-10]
そのメソッド呼び出し箇所でのパラメタのコンパイル時の型(compile-time type)
が InterfaceA / InterfaceB のいずれにも適合可能であると、
どちらを呼び出せばよいかコンパイラが判断できないためです。
同名で引数の型が異なるメソッドが複数宣言されている場合、
それらのメソッドはオーバロードされ、それぞれはまったく異なるメソッド
となります。実際に複数あるうちのどのメソッドが呼び出されるかは、
コンパイル時に決定されます。したがって、コンパイル時にどちらの型として
扱えばよいか判断できないメソッド呼び出し文はコンパイルエラーとなります。
例えば、以下のソースコードは、Test#callA() メソッドと Test#callB() メソッド
の2箇所でコンパイルエラーとなってしまいます。
interface A {}
interface B {}
class AB implements A, B {}
class Test {
void method(A a) {}
void method(B b) {}
void callA() {
AB ab = new AB();
method(ab); // method(A a)を呼びたい
}
void callB() {
AB ab = new AB();
method(ab); // method(B b)を呼びたい
}
}
この問題を解決するには、引数の型をキャストなどによって明示します。先の例であれば、Test#callA()メソッド、Test#callB()メソッドをそれぞれ以下のように変更すればコンパイルエラーは発生しなくなります。
void callA() {
AB ab = new AB();
method((A)ab); // method(A a)を呼ぶ
}
void callB() {
AB ab = new AB();
method((B)ab); // method(B b)を呼ぶ
}
[S011 Q-11]
サブクラスでメソッドをオーバライドできないようにするには?
[S011 A-11]
メソッドを final 宣言します。
以下に例を示します。
class Parent {
final void foo() {}
}
class Child extends Parent{
void foo() {} // コンパイルエラー
}
[S011 Q-12]
メソッドの名前やパラメータがなく、{} で囲まれた部分があるのですが、これはなんですか?
[S011 A-12]
インスタンス初期化子です。
インスタンス初期化子には、オブジェクトがインスタンス化される時に実行したい処理を記述します。
インスタンス初期化子に記述された内容は、スーパークラスのコンストラクタの処理が終わったあと、
自クラスのコンストラクタ内の内容の前に、処理されます。また、複数のインスタンス初期化子がある場合は、
ソース上に現れた順に処理されます。
[S011 Q-13]
メソッドの名前やパラメータがなく、static {} で囲まれた部分があるのですが、これはなんですか?
[S011 A-13]
静的初期化子です。
静的初期化子に記述された内容は、そのクラスが初めて使用されるときに実行されます。
また、複数の静的初期化子がある場合は、ソース上に現れた順に処理されます。
[S011 Q-14]
newの後ろにメソッド定義が並んでいるのですが、これはなんですか?
[S011 A-14]
匿名クラスの定義です。
詳しくは、S012-01「newの後ろにメソッド宣言が並んでいるのですが、これはなんですか?」
を参照してください。
[S011 Q-15]
メソッドをオーバーライドするときにメソッド名を間違えるとどのような
ことになりますか?
[S011 A-15]
間違えたメソッド名を持つ、新しいメソッドを定義 (実装) したことにな
ります。コンパイルエラーにはなりません。
参考記事[JavaHouse-Brewers:22959]
[S011 Q-16]
クラスメソッド (static メソッド) の呼び出し方は?
[S011 A-16]
クラス名.メソッド名(引数) とします。
例えば、
Thread.sleep(1000);
のように Thread クラスの sleep(long) メソッドを呼び出します。
参考記事[JavaHouse-Brewers:3548]
[S011 Q-17]
クラスメソッドからクラスメソッドを呼び出したところ、スーパークラス
のメソッドが呼び出されます。なぜですか?
[S011 A-17]
例えば次のコードを見て下さい。
class HideTest {
public static void main( String[] args ) {
Sub s = new Sub();
s.method1();
}
}
class Super {
static void method1() {
method2();
}
static void method2() {
System.out.println( "Super#method2()" );
}
}
class Sub extends Super {
static void method2() {
System.out.println( "Sub#method2()" );
}
}
このプログラムを実行すると、結果は "Super#method2()" と表示されます。
次に、Super クラスの method1, method2 と、Sub クラスの method2 から
static を取り除いて実行すると、結果は "Sub#method2()" となります。
クラスメソッドの呼び出しでは、コンパイル時にどのクラスのメソッドを
呼び出すかが決定しますので、
Sub#method1 = Super#method1 -> Super#method2
の順に呼び出されます。対してインスタンスメソッドの場合動的ですので、
Sub#method1 = Super#method1 -> Sub#method2
と呼び出されます。
JLS によると、クラスメソッド (static あり) の場合を hide 、インスタ
ンスメソッド (static なし) の場合を override と呼びます。
参考記事[JavaHouse-Brewers:15615]
[S011 Q-18]
クラスメソッドにすると再利用性が失われる気がするのですが?
[S011 A-18]
クラスメソッドを利用すれば、コードの可読性が上がる場合があります。し
かし、java.lang.Math クラスやユーティリティクラスのようにクラスメソッ
ドを利用 (多用) すると、そのクラスのバージョンによって再利用性が失わ
れる場合もあります。
このようなとき、以下のようなコードにしてみるといいでしょう。Singleton
パターンを利用したコーディング例です。
class Foo {
void foo() {
Urility util = Utility.getInstance();
int i = util.method();
}
}
class Utility {
private Utility instance = null;
public int method() {
...
}
public static string getInstance() {
if( instance == null ) {
instance = new Utility();
}
return instance;
}
}
参考記事[JavaHouse-Brewers:16549]
[S011 Q-19]
サブクラスでオーバーライドされても自クラスのメソッドを確実に呼ぶにはどうしたらいいですか?
[S011 A-19]
メソッドを private にしましょう。
以下のコードを見て下さい。
public class B {
public static void main( String[] args ) {
A a = new A();
a.test();
}
void test() {
this.foo();
this.bar();
}
void foo() {
System.out.println( "B" );
}
private void bar() {
System.out.println( "B" );
}
}
class A extends B {
void foo() {
System.out.println( "A" );
}
private void bar() {
System.out.println( "A" );
}
}
このプログラムは
% java B
A
B
という結果になります。
参考記事[JavaHouse-Brewers:13968]
[S011 Q-20]
オーバーライドしたメソッドがスーパークラスで削除されたとき、どうなりますか?
[S011 A-20]
もしその削除されたメソッドを呼び出せば NoSuchMethodError 例外が発生し
ます (super.メソッド名() したとき)。呼び出さなければ問題ありません。
参考記事[JavaHouse-Brewers:380]
[S011 Q-21]
Class.forName( classname ) で取得したクラスのメソッドを呼び出したいの
ですが、どうしたらいいですか?
[S011 A-21]
interface もしくは abstract クラスを作成しましょう。
interface Foo {
abstract void foo( int a );
}
もしくは
abstract Foo {
abstract void foo( int a );
}
を定義します。classname で指定するクラスはこれを implements または
extends して実装しておきます。そうすれば、
Class targetClass = Class.forName( "クラス名" );
Foo targetInstance = (Foo)targetClass.newInstance();
targetInstance.foo( 100 );
のようにして呼び出せます。
参考記事[JavaHouse-Brewers:13968]
[S011 Q-22]
オブジェクトをシリアライズするとき、メソッドの分だけサイズが大きくな
るのですか?
[S011 A-22]
なりません。シリアライズされるのはデータだけです。
参考記事[JavaHouse-Brewers:22517]
[S011 Q-23]
オブジェクトに後からメソッドを追加することはできますか?
[S011 A-23]
できません。そのような要件がある場合はデータと処理でクラスを分けま
しょう。
参考記事[JavaHouse-Brewers:22512]
[S011 Q-24]
メソッドの中でメソッドを宣言するにはどうしますか?
[S011 A-24]
メソッドの中でクラスを定義します。
void foo() {
:
a = Bar.method();
:
class Bar {
static int method() {
:
}
}
}
参考記事 [JavaHouse-Brewers:20242]
[S011 Q-25]
別パッケージのクラスで friendly メソッドがオーバーライドされてしまう
のですが?
[S011 A-25]
JDK 1.0.2 をお使いならば、VM の正しくない動作(おそらくバグ)です。
以下のコードを見て下さい。
import pack.Base;
public class Child extends Base {
public static void main( String[] args ) {
Base base = new Child();
base.callIndirectFoo( base );
}
void foo() {
System.out.println( "Child#foo()" );
}
}
package pack;
public class Base {
void foo() {
System.out.println( "pack.Base#foo()" );
}
public void callIndirectFoo( Base base ) {
base.foo();
}
}
JDK 1.0.2 でコンパイルし動作させると、"Child#foo()" が出力されます。
しかし、JDK 1.1 以降であれば "pack.Base#foo()" が出力され、こちらが
正しい動作です。
参考記事[JavaHouse-Brewers:8704]
contributor: Feiichi
コメントの送り先:Java FAQ BBS