staticなメソッド

みるくあいらんどっ! > ドキュメント > Java > じっくり学ぶ Java講座 [初心者向け・入門]


staticなメソッドの基礎

ここまで、条件式と繰り返しについて学んできました。本章ではstaticなメソッドについて学習していきます。なお、メソッドはstaticなメソッドまたはインスタンスメソッドのどちらかになります。インスタンスメソッドについては後の章で解説します。まずはこの章で、staticなメソッドについて解説していきます。

なお、中学数学や高校数学では関数(一次関数や二次関数など)を習ったかと思います。staticなメソッドとは、それをより一般化したものとも言えます。

戻り値のあるメソッド

税込み価格を計算する

さて、消費税の税込み価格を計算するプログラムを製作することにします。仕様を以下に定めます。

  • ユーザから価格の入力を受け付ける。
  • 入力された価格がマイナスの場合は異常表示する。
  • ユーザから入力された価格の税込み価格(消費税率 5%)を表示する。
  • 合わせて 2000円, 5000円について、税込み価格を表示する。

この仕様を満たすソースコードを以下のように製作しました。

G101/MySystem.java(ライブラリをそのまま利用します)

G101/G101.java

/**
 * 消費税を含んだ価格を計算します。
 */
public class G101 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		int price;
		
		price = MySystem.in.getInt("税抜き価格を入力してください");
		
		if(price < 0) {
			System.out.println("価格が異常です");
		} else {
			int taxIncludedPrice = G101.getTaxIncludedPrice(price);
			System.out.println(price + "円は税込みで " + taxIncludedPrice + "円です。");
			
			System.out.println("ちなみに、");
			System.out.println("2000円の場合は" + G101.getTaxIncludedPrice(2000) + "円になります。");
			System.out.println("5000円の場合は" + G101.getTaxIncludedPrice(5000) + "円になります。");
		}
	}
	
	/**
	 * 税込み価格を返します。
	 * @param p 税抜き価格
	 */
	private static int getTaxIncludedPrice(int p) {
		// 消費税率は 5%として計算する
		double temp = (double)p * 1.05;
		int tip = (int)temp;
		return tip;
	}
}

実行結果の例は以下の通り。

税抜き価格を入力してください? 980
980円は税込みで 1029円です。
ちなみに、
2000円の場合は2100円になります。
5000円の場合は5250円になります。

上記のソースコードで注目すべき点には、以下のものがあります。

  • (staticなメソッドである)mainメソッドの他に、もうひとつ getTaxIncludedPriceという staticなメソッドが存在する。したがって、このソースコードには staticなメソッドが 2つ存在している。
  • mainメソッドにも getTaxIncludedPriceメソッドにも、予約語staticが使用されている。
  • getTaxIncludedPriceメソッドに使用されている予約語privateについては後の章にて説明します。このソースコードにおいてはpublicと記述しても実行結果は変わりません。
  • getTaxIncludedPriceメソッドの前には、intと記述されている。getTaxIncludedPriceメソッドの戻り値の型は int型となる。
  • mainメソッドの実行中、19行目、22行目、23行目を実行している最中に getTaxIncludedPriceメソッドが呼び出される。その都度、このソースコードの 32行目から 35行目が実行される。
    • getTaxIncludedPriceメソッドは、ひとつの引数(ひきすう)をとる。引数の型は int型である。
    • 19行目では変数priceの値が、22行目では 2000が、23行目では 5000という値が、getTaxIncludedPriceメソッドに渡される。
    • 受け取った値は、int型の変数pに代入される。
    • getTaxIncludedPriceメソッド内で、税込み価格が計算され、最終的に変数tipに値が格納される。
    • getTaxIncludedPriceメソッドの最後には、予約語returnが使用されている。returnの後には税込み価格が格納されている変数tipが指定されている。
    • 結果的に、19行目、22行目、23行目ではそれぞれの税込み価格を取得することができる。
  • 仮に消費税率を変更する必要があった場合、1.05の 1ヶ所を変更するだけでプログラムの修正を終えることができる。計算を 3回実施しているにも関わらず、変更は 1ヶ所で済む。

なお、staticなメソッドについては、以下のことも合わせて覚えてください。

  • 今回、mainメソッド内では変数を priceと名づけ、getTaxIncludedPriceメソッドでは引数の変数を pと名づけていますが、実際には同じ変数名としても大丈夫。変数のスコープについて後の章で解説しますが、仮に同じ変数名にしたとしても mainメソッドの変数と getTaxIncludedPriceメソッドの変数とは別物として扱われます。
  • 今回、mainメソッドからの呼び出し部分では G101.getTaxIncludedPrice(~)と記述しました。実はG101.の部分は省略可能で、getTaxIncludedPrice(~)と記述しても大丈夫です。そして、世の中一般には省略することが普通なのですが、本ウェブサイトでは省略を一切しないで記述していきます。というのも、省略すると、staticなメソッドと(後の章で登場する)インスタンスメソッドとの違いが分かりづらくなるため、という理由があります。

階乗を求める

仕様を以下に定めます。

  • 1の階乗から 10の階乗までを表示する。なお階乗については以下の通り。
    • 階乗とは該当する数から 1までを乗算したもの。「5の階乗」とは 5×4×3×2×1、つまり 120 のこと。
  • 階乗の計算部分を staticなメソッドに分離する。
  • 計算結果の値が大きくなるため、階乗の値を格納する変数には long型を使用することにする。今回は 10の階乗までなので int型で問題ないのだけど、今後のことを考えて念のため。

この仕様を満たすソースコードは以下の通り。

G102/G102.java

/**
 * 1~10の階乗を表示します。
 */
public class G102 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		for(int i = 1; i <= 10; i ++) {
			long value = G102.factorial(i);
			System.out.println(i + "の階乗は " + value);
		}
	}
	
	/**
	 * 指定した数の階乗を求めます。なお、0以下の値を指定した場合、1を返します、注意。
	 * @param number 数
	 * @return 階乗の値
	 */
	private static long factorial(int number) {
		long value = 1;
		for(int i = number; i >=1 ; i --) {
			value *= i;
		}
		
		return value;
	}
}

実行結果は以下の通り。

1の階乗は 1
2の階乗は 2
3の階乗は 6
4の階乗は 24
5の階乗は 120
6の階乗は 720
7の階乗は 5040
8の階乗は 40320
9の階乗は 362880
10の階乗は 3628800

mainメソッド内の変数 iと factorialメソッド内の変数 iは、たまたま変数名が一緒なだけで別物の変数として扱われていることも分かるかと思います。

mの n乗を求める

仕様を以下に定めます。

  • ユーザから 2つの数値を入力してもらう。
  • 1つ目の値を m、2つ目の値を nとした場合の、mの n乗を表示する。ただし、nがマイナスの場合は異常表示する。また、0の 0乗についても異常表示する。
  • 合わせて、「3の 5乗」と「2の 10乗」の計算結果を表示する。

この仕様を満たすソースコードは以下の通り。

G103/MySystem.java(ライブラリをそのまま利用します)

G103/G103.java

/**
 * mのn乗を計算します。
 */
public class G103 {
	
	public static void main(String[] args) {
		int a;
		int b;
		int value;
		
		a = MySystem.in.getInt("1つ目の数を入力してください");
		b = MySystem.in.getInt("2つ目の数を入力してください(0以上)");
		
		if(b < 0) {
			System.out.println("2つ目の数がマイナスです。");
		} else if(a == 0 && b == 0) {
			System.out.println("0の 0乗は計算できません。");
		} else {
			value = G103.power(a, b);
			System.out.println(a + "の " + b + "乗は " + value + "です。");
			
			System.out.println("ちなみに、");
			System.out.println("3の 5乗は" + G103.power(3, 5) + "。");
			System.out.println("2の 10乗は" + G103.power(2, 10) + "。");
		}
	}
	
	/**
	 * mの n乗を返します。
	 * なお、nがマイナスの場合、計算結果が狂います、注意。
	 * また、0の 0乗についても、計算してしまいます、注意。
	 * @param m 値
	 * @param n かける回数
	 * @return 計算結果
	 */
	private static int power(int m, int n) {
		int value = 1;
		for(int i = 0; i < n; i ++) {
			value *= m;
		}
		
		return value;
	}
}

実行結果の例は以下の通り。

1つ目の数を入力してください? 5
2つ目の数を入力してください(0以上)? 3
5の 3乗は 125です。
ちなみに、
3の 5乗は243。
2の 10乗は1024。
1つ目の数を入力してください? 3
2つ目の数を入力してください(0以上)? 0
3の 0乗は 1です。
ちなみに、
3の 5乗は243。
2の 10乗は1024。
1つ目の数を入力してください? 4
2つ目の数を入力してください(0以上)? -2
2つ目の数がマイナスです。
1つ目の数を入力してください? 0
2つ目の数を入力してください(0以上)? 0
0の 0乗は計算できません。

戻り値のないメソッド

戻り値のないメソッドの基礎

先の 3つのプログラムでは、戻り値のあるメソッドを見てきました。ここでは戻り値のないメソッドについて解説していきます。以下のような形を使用します。

	private static void メソッド名(引数, 引数, …) {
		処理;
	}

見ての通り、予約語voidを使用します。それが戻り値のないメソッドの特徴ということになります。なお、staticについては後の章で解説します、今は常に staticを使用します。また privateについても後の章で解説します、今のうちは privateを publicと書き換えても実行結果は変わりありません。

指定回数だけ文字列を表示する

仕様を以下に定めます。

  • ユーザから数字の入力を受け付ける。
  • 入力された回数だけワンと表示する。0以下の場合は異常表示する。
  • 合わせて、ワンワン、ワン、ワンワンワンワンと表示する。

この仕様を満たすソースコードは以下の通り。

G104/MySystem.java(ライブラリをそのまま利用します)

G104/G104.java

/**
 * 指定回数だけ文字列を表示します。
 */
public class G104 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		int count;
		
		count = MySystem.in.getInt("回数を入力してください");
		G104.cryWan(count);
		
		System.out.println();
		
		G104.cryWan(2);
		G104.cryWan(1);
		G104.cryWan(4);
	}
	
	/**
	 * 指定回数だけ文字列「ワン」を表示します。
	 * @param count 回数
	 */
	private static void cryWan(int count) {
		for(int i = 0; i < count; i ++) {
			System.out.print("ワン");
		}
		System.out.println();
	}
}

実行結果の例は以下の通り。

回数を入力してください? 10
ワンワンワンワンワンワンワンワンワンワン

ワンワン
ワン
ワンワンワンワン

void型のメソッド cryWanは、戻り値を返す必要がありません。したがって、予約語returnは必ずしも記述する必要がありません。言い換えれば、今回の場合、31行目の 32行目の間に、return;が省略されていると考えることもできます。

これまで使用してきた mainメソッドも、voidの文字が使用されています。したがって、mainメソッドも戻り値を返す必要がなく、今まで予約語returnを使用する必要に迫られなかった、ということでもあります。

メソッドを入れ子にする

メソッドを入れ子にする

あるメソッドがその中で別のメソッドを呼び、さらにそのメソッドが別のメソッドを呼ぶ…という構造を組み上げることで、より大きな規模のプログラムを、簡単に製作できるようになっていきます。

また、1つのメソッドの中身があまりにも巨大だと、それを人間が理解するのは大変です。ある一定の規模になったら、別のメソッドに分離独立させていくと、比較的読みやすいソースコードになる可能性が高まります。

メソッドを入れ子にした例をひとつサンプルとして挙げてみます。

うるう年かどうかを判定する

以前、if文の解説の際に、うるう年かどうかを判定するプログラムを作りました。今回はそのプログラムを改めて製作してみることにします。仕様を以下に定めます。

  • 西暦をユーザに質問する。
  • 入力された値がマイナスの場合は、異常表示する。
  • その年がうるう年であるかどうか表示する。詳細は次の通り。
    • 400で割り切れるとき(例えば 2000)、うるう年である。
    • 100で割り切れるとき(例えば 1900)、うるう年ではない。
    • 4で割り切れるとき(例えば 2012)、うるう年である。
    • 上記以外のとき(例えば 2013)、うるう年ではない。
  • 合わせて、1900年、2000年、2012年、2013年のうるう年の判定結果を表示する。

この仕様を満たすソースコードは以下の通り。

G105/MySystem.java(ライブラリをそのまま利用します)

G105/G105.java

/**
 * うるう年を判定します。
 */
public class G105 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		int year;
		
		System.out.println("[うるう年判定プログラム]");
		
		year = MySystem.in.getInt("西暦を入力してください");
		
		if(year < 0) {
			System.out.println("マイナスの値が入力されました。");
		} else {
			if(G105.isLeapYear(year)) {
				System.out.println("入力された " + year + "年はうるう年です。");
			} else {
				System.out.println("入力された " + year + "年はうるう年ではありません。");
			}
			System.out.println("ちなみに、");
			
			G105.printLeapYear(1900);
			G105.printLeapYear(2000);
			G105.printLeapYear(2012);
			G105.printLeapYear(2013);
		}
	}
	
	/**
	 * 指定した年がうるう年かどうかを表示します。
	 * @param year 年
	 */
	private static void printLeapYear(int year) {
		if(year < 0) {
			System.out.println("西暦がマイナスです。");
		} else if(G105.isLeapYear(year)) {
			System.out.println(year + "年はうるう年です。");
		} else {
			System.out.println(year + "年はうるう年ではありません。");
		}
	}
	
	/**
	 * 指定した年がうるう年かどうかを判定します。
	 * 年がマイナスでも計算してしまいます、注意。
	 * @param year 年
	 * @return うるう年の場合、true
	 */
	private static boolean isLeapYear(int year) {
		if(year % 400 == 0) {
			return true;
		} else if(year % 100 == 0) {
			return false;
		} else if(year % 4 == 0) {
			return true;
		} else {
			return false;
		}
	}
}

実行結果の例は以下の通り。

[うるう年判定プログラム]
西暦を入力してください? 1996
入力された 1996年はうるう年です。
ちなみに、
1900年はうるう年ではありません。
2000年はうるう年です。
2012年はうるう年です。
2013年はうるう年ではありません。

mainメソッドが printLeapYearメソッドを呼び出し、printLeapYearメソッドが isLeapYearメソッドを呼び出していることが分かるかと思います。

メソッドの無限呼び出し

あるメソッドが別のメソッドを呼び、そのメソッドが別のメソッドを呼び…それが無限ループのようになってしまったら、実行結果はどうなるのかを実験してみます。

サンプルプログラムは以下の通り。

G106/G106.java

/**
 * メソッドの無限呼び出しをします。
 */
public class G106 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		// 無限ループするメソッドを呼び出す
		G106.loop();
	}
	
	/**
	 * メソッドを無限に呼び出します。
	 */
	private static void loop() {
		G106.loop();
	}
}

実行結果は以下の通り。

Exception in thread "main" java.lang.StackOverflowError
	at G106.loop(G106.java:19)
	at G106.loop(G106.java:19)
	at G106.loop(G106.java:19)
	(省略)
	at G106.loop(G106.java:19)

プログラムは延々と終了しないのではなく、強制終了となります。この StackOverflowErrorという強制終了については、ずっと後の「エラーと例外」のところで解説します。無限のメソッド呼び出しは強制終了することについては覚えておいてください。

なお、「実行時に落ちる」例は、ずっと以前、「ハローワールド」を作成したところでさらっと紹介して以来、2例目となります。あのときは ArithmerticExceptionという Exception系の強制終了でした。今回は、StackOverflowErrorという Error系の強制終了となります。この Exception系と Error系の違いについては、後の章(例外という章)にて詳しく解説していきます。

C言語の staticと Javaの static

Javaは、C言語の文法の影響を大きく受けていると思います。ただ、予約語 staticについては、C言語での使われ方と Javaでの使われ方はまったく異なります。C言語を学習済みの方は、予約語 staticに関しては、先入観を排除して把握したほうが理解は早まると思います。

staticなメソッドを利用する

Javaには、たくさんのメソッドが用意されています。その中には staticなメソッドもあれば、staticでないメソッド(=インスタンスメソッド)もあります。一部の staticなメソッドについてはここまで学んできたことを踏まえて利用することができます(なお、大半の staticなメソッドについては、予約語importを使った記述を必要としますので、詳細は後の章に記述します)。

これまで時折、Math.randomメソッドを使用してきました。以下の感じで。

		double a = Math.random();

これは Mathクラスに存在する staticなメソッド randomを利用していたのです。

Java SE 7(Java 1.7)の Mathクラスの説明書(API)を見てみることにします。次のリンクをクリックしてください→Mathクラス。この中に randomメソッドの記述を発見できたでしょうか。randomメソッドの説明文の中身については専門的な話なので分からなくて構いません。ただ、randomメソッドが staticで、また戻り値の型が double型であることは分かると思います。

実は、Mathクラスに登録されているすべてのメソッドは staticです。したがって、randomメソッドと同じような感覚で、Mathクラスの説明書(API)に書かれた他のメソッドを利用することができます。mの n乗を求めたり、平方根を求めたり、絶対値を求めたりできるメソッド、三角関数や対数を扱うためのメソッドが用意されています。説明書(API)の記述を頼りにしながら試しに利用してみると、勉強になるかと思います。

オーバーロード

先ほどの Mathクラスを見ていると、同じメソッド名にも関わらず、引数の型が異なるものが別物として扱われていることが分かります。例えば、absメソッドには以下の 4つがあることが分かります。

  • static double abs(double a)
  • static float abs(float a)
  • static int abs(int a)
  • static long abs(long a)

このように引数の型が異なる場合の他、引数の数が異なったり、(複数の引数がある場合には)引数の型の並び順が違う場合、Javaでは、同じメソッド名でも別物として扱われます。このことをオーバーロードと呼びます。なお、しばらく後の章で、オーバーライドという言葉が登場しますので、混同しないように注意してください。

引数の数が異なるサンプルプログラムを以下に挙げます。

G107/G107.java

/**
 * オーバーロードを確認します。
 */
public class G107 {
	
	/**
	 * メインメソッド。
	 * @param args 引数
	 */
	public static void main(String[] args) {
		G107.cryWan(2);
		
		G107.cryWan();
		
		G107.cryWan(4);
	}
	
	/**
	 * 指定回数だけ文字列「ワン」を表示します。
	 * @param count 回数
	 */
	private static void cryWan(int count) {
		for(int i = 0; i < count; i ++) {
			System.out.print("ワン");
		}
		System.out.println();
	}
	
	/**
	 * 文字列「ワン」を表示します。
	 */
	private static void cryWan() {
		G107.cryWan(1);
	}
}

実行結果は以下の通り。

ワンワン
ワン
ワンワンワンワン

このサンプルプログラムの場合には、引数 1個の cryWanメソッドと引数の無い cryWanメソッドが存在します。今回の場合、引数の無い cryWanメソッドは、事実上、引数の省略を手助けするような形になっています。

最終更新: 2014/08/01 , 公開: 2013/02/03
▲top