メソッド

これまで何気なく使ってきたメソッドに関して詳しく見ていきましょう。

関数

関数とは元は数学の用語であり、Wikipediaによれば次のように説明されています。

関数(かんすう、function)とは、ある変数に依存して決まる値あるいはその対応を表す式のことである。

http://ja.wikipedia.org/wiki/%E9%96%A2%E6%95%B0_%28%E6%95%B0%E5%AD%A6%29

一般的に関数は、f(x) などと記述し、fが関数の名前であり、xが変数です。
変数は1つであるとは限らず、g(x, y, z) のように複数が記述されることもあります。
数学の世界では一般的にはなんらかの値を返し、y = f(x) というような記述がされます。

プログラミング言語の世界での関数(function)もほぼ同じような概念であり、ある変数(引数)に依存してなんらかの値(返値)を返します。
引数が存在しないケースや戻り値が存在しないケースなど異なる点もありますが、考え方としては同じと言えます。

ところで、Javaでは関数(Function)は存在しません。
似た概念としてメソッド(Method)があるのですが、関数(Function)とは呼ばずに、メソッド(Method)と区別して呼んでいます。

メソッド

メソッド(Method)はクラスに属している関数ですから、関数でないわけではありません。
ですが、オブジェクト指向プログラミングにおいては、「クラスに属している」という点が重要となります。
何故ならば、オブジェクト指向プログラミングではプログラムの構成単位はクラスであり、クラスのインスタンス同士を組み合わる事でプログラムとなるからです。

関数といった場合は、数学の関数と同じように不変なものです。
したがって、クラスやインスタンスといった仕組みとは関係なく、どこからでも好きなタイミングで呼び出されねばなりません。
Javaではこのようなどのクラスにも属さない関数(グローバル関数)は定義することができません。

メソッドの引数

メソッドの引数には、プリミティブ型変数(いわゆる一般的な値)だけではなく、参照型変数も定義することができます。
プログラムの中でメソッドが実行されたならば、メソッドの引数で定義された変数はメモリ(スタック領域)に確保され、呼出元で使用していた変数のメモリ(スタック領域)からコピーされます。

(資料作成中!)

つまり、引数がプリミティブ型の場合、メソッド内で引数で指定された変数を変更しても呼び出し側には影響がありませんでした。

    public void foo(int i)
    {
        // 呼び出し元の整数は変更されない
        i = i + 10;
    }

このようにメソッド(関数)に変数を渡して、その変数の値をメソッド側で完全にコピーする動きは、変数の値渡しと呼ばれます。
値渡しではその変数の中身がコピーされているので、呼び出し元には影響を与えず、セキュアなプログラムを書きやすくなります。

これは引数が参照型変数であった場合でも変わりません。
メソッド(関数)が実行されると参照型変数がスタック領域に確保され、引数として渡された参照型変数の値がコピーされます。

    public void foo(Foo foo)
    {
    }

しかし、この時にコピーされる参照型変数の値はインスタンスではないことに注意して下さい。
参照型変数はインスタンスを入れる入れ物ではありません。
インスタンスへの参照を値として参照型変数は持っているのですから、コピーされた参照型変数は同じインスタンスを参照している状態となります。
したがって、引数で渡されたクラスのインスタンスに対する操作を行った場合、その影響は呼び出し元にも反映されます。

    public void foo(Foo foo)
    {
        // この操作はfooの指すインスタンスへの操作
        // よって呼び出し元で参照しているインスタンスにも影響は及ぶ
        foo.hoo();
    }

尚、この値渡しを行う場合、変数のコピーが行われる為にメソッド呼び出しには少なからずオーバーヘッドはあります。
我々がそのオーバーヘッドを気にしてプログラムを組まなくてはならないほど、現在の実行環境は貧弱ではありませんが、引数があまりにも多くなるとメソッドの見通しが悪くなり、コードの可読性が低くなるでしょう。
引数で持ちまわすのではなくてメンバ変数として持てないか?引数をまとめて1つのクラスに定義できないか、などを検討するべきです。

参照渡し

値渡しに対する用語として参照渡しと呼ばれる用語がありますが、Javaではクラスを引数にした場合は参照渡しであるとよく誤解されています。
参照渡しの参照とは実体(インスタンスやデータ)のメモリ上の場所(アドレス)の事を指します。
したがって、アドレスだけを渡し、関数ではそのアドレスへの直接操作を行う為に関数の呼び出し元に影響を与えます。

ですが、Javaではメソッドの引数は全てメソッド実行時にコピーされる為、参照渡しではなく値渡しです。
次のメソッドを考えて見ましょう。

    public void foo(Foo f)
    {
        f = new Foo(); // インスタンス2
    }
    Foo a = new Foo(); // インスタンス1
    Foo b = a;
    foo(a);
    if(a == b)
    {
        System.out.println("参照渡しではありません!");
    }

fooが参照渡しであると仮定します。
foo(a)が呼ばれた時点で、aのアドレスがメソッドfooのfに渡されます。
fのアドレスに新しく生成したインスタンス2が代入されました。
すると、呼び出元のaはインスタンス2でなくてはなりません。

戻り値

メソッドに指定できる戻り値も、プリミティブ型とクラスの両方を指定できます。
さらに、戻り値が必要でない場合はvoidを指定することで、戻り値のないメソッドを定義できます。

return文

メソッドで戻り値を指定した場合、return文を用いて値を返す必要があります。
return文が実行されると、そのメソッド内の残りの処理は省略され、直ちに呼び出し元へ制御を戻します。
return文は必ず1つは書かなければなりませんが、到達不能コードがない限りは幾つ書いても構いません。

    return 戻り値;

戻り値として呼び出し元に返されるのは、変数に格納されている値です。
したがって、プリミティブ型ならばその値が、参照型変数であればインスタンスへのアドレスが呼び出し元へ返されます。

尚、次のようにreturn文を書くことも可能です。

    return new Foo();

この場合もFooのインスタンスが生成され、そのインスタンスへの参照アドレスが呼び出し元に返されます。
このコードは次のように書いた場合と同じことになります。

    Foo result = new Foo();
    return result;

return文で指定する値は戻り値として指定した型と同じであるか、暗黙的な型変換が可能でなくてはなりません。
したがって、クラスの場合は戻り値で指定したクラスかそのサブクラスである必要があります。

また、戻り値の型にvoidを指定した場合は、戻り値のないreturn文を記述することができます。

    return;

このreturn文が実行された時点でメソッドの実行は終了となり制御を呼び出し元に戻す為、特定の条件で処理を中断するときに使用されます。
メソッドの最後に記述しても当然ながら意味はありません。