メソッド

<概要>メソッドを学びます。変数の有効範囲についても考察。やっとメソッド解禁だー(笑)!

例題8-1 半額の計算をするメソッドを定義する。

○プログラム

public class Discount2{
  public static void main(String[] args){
    int p,q;
    p=1000;
    q=halve(p);
    System.out.println("元の値段が"+p+"円なら、半額だと"+q+"円になります。");
  }
  public static int halve(int n){
    return n/2;
  }
}


○実行結果

D:\atsushi\Java\List8-2>java Discount2
元の値段が1000円なら、半額だと500円になります。
-- Press any key to exit (Input "c" to continue) --

○解説

public はクラスのアクセス制御キーワードです。
C++言語みたいにまとめて書くんじゃなく、
一つ一つのメソッド、フィールドにアクセス制御キーワードを付与していきます。
省略した場合は、「なし」という別のアクセス制御キーワードを表すことになります。
詳しくは「パッケージ」で解説します。

static はクラスメソッドであることを示しています。
詳しくは「クラスとインスタンス」で解説します。

メソッドの名前には、英語の大文字( A 〜 Z )、小文字( a 〜 z )、下線文字( _ )、数字( 0 〜 9 )、
それから Unicode に登場する数多くの文字が使えます。
ただし、数字から始まる名前は使えません。


ところで、C言語では関数を呼び出す前に宣言する必要がありました。
このプログラムでは、宣言する前に呼び出しちゃってますよね。
これって大丈夫なの?と思われたかも知れません。
私も一瞬そう思いました。
しかし、C++言語のクラスではメンバー関数の並び順に意味はありません。
Java言語のクラスもC++言語のクラスと同じだと考えれば、
ちょっと違和感は残りますが、納得できるのではないかと思います。

例題8-2 引数で指定した個数だけ「*」を表示して改行するメソッドを定義する。

○プログラム

public class Graph1{
  public static void main(String[] args){
    printGraph(10);
  }
  public static void printGraph(int x){
    for(int i=0;i<x;i++){
      System.out.print("*");
    }
    System.out.println("");
  }
}


○実行結果

D:\atsushi\Java\List8-3>java Graph1
**********
-- Press any key to exit (Input "c" to continue) --

○解説

戻り値のないメソッドは return 文を省略することが出来ます。
省略せずに return; とだけ書くことも出来ます。

main メソッドは return 文を省略しているのですね。

例題8-3 引数の累乗を計算するメソッドを定義する。

○プログラム

public class Power1{
  public static void main(String[] args){
    System.out.println(getPower(2,3));
  }
  public static int getPower(int x,int n){
    int y=1;
    for(int i=0;i<n;i++){
      y*=x;
    }
    return y;
  }
}


○実行結果

D:\atsushi\Java\List8-4>java Power1
8
-- Press any key to exit (Input "c" to continue) --

○解説

引数の個数は幾つでも( 0 個でも)全然問題ありません。

例題8-4 引数で指定した文字を引数で指定した個数だけ表示して
        改行するメソッドを定義して下さい。
        文字の指定は省略できるものとし、省略した場合は「*」を表示することにします。


○プログラム

public class Graph2{
  public static void main(String[] args){
    printGraph(10);
    printGraph(5,'+');
  }
  public static void printGraph(int x){
    for(int i=0;i<x;i++){
      System.out.print("*");
    }
    System.out.println("");
  }
  public static void printGraph(int x,char c){
    for(int i=0;i<x;i++){
      System.out.print(c);
    }
    System.out.println("");
  }
}


○実行結果

D:\atsushi\Java\p198>java Graph2
**********
+++++
-- Press any key to exit (Input "c" to continue) --

○解説

メソッドを多重定義しています。

○補足

ディフォルト引数を指定したらどうなるでしょうか?

public static void printGraph(int x,char c='#'){

//コンパイル

■D:\atsushi\Java\p198> javac Graph2.java
Graph2.java:12: ')' がありません。
public static void printGraph(int x,char c='#'){
                          ^
Graph2.java:18: ';' がありません。
}
^
エラー 2 個

どうやらディフォルト引数は使えないみたいですね。

例題8-5 変数の有効範囲。

○プログラム&解説

//例1

public class NameSpace{
  public static void main(String[] args){
    int i;
    for(int i=0;i<3;i++){
      System.out.println(i);
    }
  }
}


//コンパイル

■D:\atsushi\Java\p192> javac NameSpace.java
NameSpace.java:4: i は main(java.lang.String[]) で定義されています。
for(int i=0;i<3;i++){
    ^
エラー 1 個

//解説

for 文の引数として宣言された変数は for ループ内部でしか使うことが出来ません。
つまり、ブロック内部で宣言されているも同然です。
それならば、答えのような変数宣言も許されるような気がしますが、
コンパイラが教えているようにエラーです。
何故ならば、3行目の変数は for ループ内部でも有効だからです。

//例2

public class NameSpace{
  public static void main(String[] args){
    for(int i=0;i<3;i++){
      System.out.println(i);
    }
    int i=10;
    System.out.println(i);
  }
}


//実行結果

D:\atsushi\Java\p192>java NameSpace
0
1
2
10
-- Press any key to exit (Input "c" to continue) --

//解説

例1と変数宣言する順番が逆転している場合です。
C言語、C++言語プログラマの私には、むしろこれがエラーにならない方が不思議ですが、
for 文の引数として宣言された変数はブロック内部で宣言されていると見なせるんでしたね。

//例3

public class NameSpace{
  public static void main(String[] args){
    for(int i=0;i<3;i++){
      System.out.println(i);
    }
    {int i=10;}
    System.out.println(i);
  }
}


//コンパイル

■D:\atsushi\Java\p192> javac NameSpace.java
NameSpace.java:7: シンボルを解決できません。
シンボル: 変数 i
場所 : NameSpace の クラス
System.out.println(i);
            ^
エラー 1 個

//解説

C言語、C++言語ではちゃんとした意味があるプログラムでも、
Java言語ではただのエラー。
しかし、これは名前の衝突が起きているわけではありません。
これより、ブロック内部で宣言された変数の有効範囲はブロック内部だけである
ということが分かります。

//例4

public class NameSpace{
  int i;
  public static void main(String[] args){
    for(int i=0;i<3;i++){
      System.out.println(i);
    }
  }
}


//実行結果

D:\atsushi\Java\p192>java NameSpace
0
1
2
-- Press any key to exit (Input "c" to continue) --

//解説

実は2行目の int i; は、変数ではなくフィールドです。
メソッド外部で宣言されているものはフィールドと呼ばれます。
フィールドと変数は別物なので名前の衝突は起こりません。

//例5

public class NameSpace{
  public static void main(String[] args){
    for(int i=0;i<3;i++){
      System.out.println(i);
    }
  }
  public static void func(){
    for(int i=4;i<6;i++){
      System.out.println(i);
    }
  }
}


//実行結果

D:\atsushi\Java\p192>java NameSpace
0
1
2
-- Press any key to exit (Input "c" to continue) --

//解説

違うメソッドで同じ変数を宣言しています。
当たり前ですが、問題ありません。

○まとめ

ブロック内部で宣言された変数の有効範囲はブロック内部だけである。
より内部のブロックであっても同じ変数を宣言することは出来ない。
変数とフィールドは別物なので、同じ名前のものを宣言することが出来る。


○補足

変数の初期化を忘れた場合どうなるでしょうか?

public class NameSpace{
  public static void main(String[] args){
    int i;
    System.out.println(i);
  }
}


コンパイラはご丁寧(お節介?)にもエラーとして知らせてくれます。
警告ではないので、もちろん実行できません。
流石に、誤りを犯しにくい言語と言われるだけのことはあります。

■D:\atsushi\Java\p192> javac NameSpace.java
NameSpace.java:4: 変数 i は初期化されていない可能性があります。
System.out.println(i);
            ^
エラー 1 個


戻る / ホーム