QA@IT
この質問・回答は、@ITの旧掲示板からインポートされたものです。

メソッドの引数の参照渡しはできますか?

言語仕様書を当たってみましたが、この点について書かれていないようなのでお尋ねします。
以下の C++ コードの3行目のように、参照渡しにする方法があれば教えてください。
Java コードでは8行目に相当します。

[C++]
01| #include
02|
03| void setFalse(bool& b) // 参照渡し
04| {
05| b = false;
06| }
07|
08| void main(void)
09| {
10| bool b = true;
11| setFalse(b);
12| printf("%s\n", b ? "true" : "false"); // false と表示される
13| }

[Java]
01| class foo {
02| static void main(String[] args) {
03| boolean b = true;
04| setFalse(b);
05| System.out.println(b); // true と表示される
06| }
07|
08| static void setFalse(boolean b) { // 参照渡しにしたい
09| b = false;
10| }
11| }

質問者:katsum

回答

Javaではメソッドパラメタはすべて値渡しです。

投稿者:miki

編集 履歴 (0)

基本データ型を「参照渡し」したいということですか?

参照型(クラスのインスタンス)と違って基本データ型は「値渡し」なのです。

どうしても基本データ型をメソッド内部で変更したいということなら
基本データ型の配列にしてみましょう。。。

public class NewFoo {

public static void main(String[] args) {
boolean[] b = new boolean[1];
b[0] = true;
setFalse(b);
System.out.println(b[0]); // false と表示される

}

public static void setFalse(boolean[] b) {
b[0] = false;
}

}

解決になってないかぁ

投稿者:toshi

編集 履歴 (0)

Javaでは基本的に全て参照渡しです。
ただ、
booleanやintなどのプリミティブな型に限って値渡しになります。

booleanを参照渡しで使いたいのであれば
Booleanクラスを使えばよいのではないでしょうか。

class foo {
static void main(String[] args) {
Boolean b = new Boolean(true);

setFalse(b);
System.out.println(b); // true と表示される
}

static void setFalse(Boolean b) { // 参照渡しにしたい
b = new Boolean(false);

投稿者:TOTO

編集 履歴 (0)

この例のBooleanだと無理ですね。booleanはプリミティブなので値渡しですし、オブジェクトのBooleanにしても受け取ったオブジェクトの内部を変えるメソッドがAPIに見当たらないので無理です。

ただし、自分でMyBooleanなどどオブジェクトを定義し、setFalse()などとすれば大丈夫です。TOTOさんの例ですと参照渡しにはなっていますが、あまり意味がないような・・・。(結局、参照先のオブジェクトはいじられてないため)

プリミティブ型(7つでしたっけ?)以外のものはすべてオブジェクトなので勝手に参照渡しになります。(ただし、String型などのimmuneなオブジェクトは要注意)どうしてもプリミティブを参照渡ししたいときには自分でオブジェクトを作らないとだめです。(APIを使用することも可ですが)

投稿者:H2

編集 履歴 (0)

直接の回答にはなっていませんが…
言語仕様 8.4.1 Formal Parameters には以下の様に書かれています。

When the method or constructor is invoked (§15.12), the values of the actual argument expressions initialize newly created parameter variables, each of the declared Type, before execution of the body of the method or constructor.

参照型の変数には文字通りインスタンスへの参照が保持されます。
したがってメソッドの引数が参照型の場合には、呼出側の変数が
保持する参照値によってメソッド引数が初期化される(参照値が
引数にコピーされる)わけです。
このようにJavaではメソッドの引数は*値渡し*になります。

参照型の場合、見た目には参照渡しに見えますけどね。

投稿者:てくてく

編集 履歴 (0)

このようにJavaではメソッドの引数は*値渡し*になります。

それを言うと、CもC++も同じですよ・・・。結局は参照先のアドレス(もしくはいわゆるポインタの持つ値)を「数値」として渡さなければいけないわけですから。

投稿者:H2

編集 履歴 (0)

たくさんのコメントありがとうございました。
プリミティブ型は参照渡し出来ないということですね。
そのことが確認できただけでも十分です。
どうもありがとうございました。

投稿者:katsum

編集 履歴 (0)

プリミティブ型は参照渡し出来ないということですね。

繰り返しになりますが、オブジェクト参照型も参照渡しできません。
Javaにはプリミティブ型とオブジェクト参照型しかないので、すべてが値渡しになります。

もし、それができたらメソッド内部から呼び出し側のオブジェクト参照型のローカル変数を書き換えることができてしまいます(オブジェクトを書き換えるのではなく、変数の内容を書きかえることに注意)。

「プログラミング言語Java第3版」に良い説明があるので引用します。
(2.6.4 パラメータの値より)

メソッドのパラメータはすべて「値渡し」です。
(略)
パラメータがオブジェクト参照の場合には、オブジェクトそのものではなく、オブジェクト参照が「値渡し」されるのに注意してください。
(略)
プログラミング言語のデザインでは、用語「参照渡し」は、関数に引数が渡される場合に、呼び出された関数が元の値への参照を得るのであり、値のコピーを得るのではないのが正しい意味です。もし、関数がそのパラメータを修正すると、引数とパラメータはメモリ上の同じ領域を使用しますので、呼び出し側の値も変更されてしまいます。
--引用終わり

この本には、図の解説と値渡しを説明したサンプルプログラムも載っています。

投稿者:miki

編集 履歴 (0)

mikiさん
サンプルを作って試してみました。確かにオブジェクトも値渡しになりますね。
参考までに C++ のサンプルも作ってみました。こちらは値渡し/参照渡しどちらも可能ですね。

[Java]
class MyBoolean {
boolean b;
}

class foo {
static void main(String[] args) {
MyBoolean mb = new MyBoolean();
mb.b = true;
setFalse(mb);
System.io.println(mb.b); // true
}

static void setFalse(MyBoolean mb) {    // 値渡し
    mb.b = false;
}

}

[C++]
#include

class MyBoolean {
public:
bool b;
};

void setFalseVal(MyBoolean mb) { // 値渡し
mb.b = false;
}

void setFalseRef(MyBoolean& mb) { // 参照渡し
mb.b = false;
}

void main(void) {
MyBoolean mb;

mb.b = true;
setFalseVal(mb);
printf("%s\\n", mb.b ? "true" : "false");   // true

mb.b = true;
setFalseRef(mb);
printf("%s\\n", mb.b ? "true" : "false");   // false

}

投稿者:katsum

編集 履歴 (0)

上の Java に関する書き込みは間違ってました。どうも失礼しました。

上の Java コードは "System.io.println" となっているように、ここでコンパイルエラーが発生していたのに気付かず、その前にコンパイルが通った foo.class を実行していたようです。
改めて以下のコードで試してみましたら、false と表示されますね。
上の C++ の参照渡しの例と同じ動作になりました。
mikiさんがおっしゃる値渡しというのは、(C++ でいう参照渡しと同じ)この動作のことでよろしいのでしょうか?

class MyBoolean {
boolean b;
}

class foo {
static void main(String[] args) {
MyBoolean mb = new MyBoolean();
mb.b = true;
setFalse(mb);
System.out.println(mb.b); // false
}

static void setFalse(MyBoolean mb) { // 値渡し?参照渡し?
mb.b = false;
}
}

投稿者:katsum

編集 履歴 (0)

参考までに C++ でアドレスを表示させた例も載せておきますね。
実行結果を見ると、main と setFalseRef では mb のアドレスが同じなので、参照渡しになっていることがわかります。C++屋さんは、こういうのを参照渡しと呼んでます。
C++ 屋さんなので、基準が C++ になってしまい申し訳ありません。

[C++]
#include

class MyBoolean {
public:
bool b;
};

void setFalseVal(MyBoolean mb) {
mb.b = false;
printf("address of mb in setFalseVal:%p\\n", &mb);
}

void setFalseRef(MyBoolean& mb) {
mb.b = false;
printf("address of mb in setFalseRef:%p\\n", &mb);
}

void main(void) {
MyBoolean mb;
printf("address of mb in main:%p\\n", &mb);

mb.b = true;
setFalseVal(mb);
printf("%s\\\\n", mb.b ? "true" : "false");

mb.b = true;
setFalseRef(mb);
printf("%s\\\\n", mb.b ? "true" : "false");

}

[実行結果]
address of mb in main:0x7ffff72b
address of mb in setFalseVal:0x7ffff724
true
address of mb in setFalseRef:0x7ffff72b
false

投稿者:katsum

編集 履歴 (0)

katsumさんの書き込み (2002-03-15 14:06) より:

mikiさん

サンプルを作って試してみました。確かにオブジェクトも値渡しになりますね。

[Java]

class MyBoolean {

boolean b;

}

class foo {

static void main(String[] args) {

  MyBoolean mb = new MyBoolean();

  mb.b = true;

  setFalse(mb);

  System.io.println(mb.b);    // true

}

static void setFalse(MyBoolean mb) { // 値渡し

  mb.b = false;

}

}

このJavaプログラムの出力は"false"になるはずです。
mbは同じオブジェクトを指し示していますから、setFalseはmb.bを変更します。

オブジェクトは値渡しになりません。オブジェクト参照が値渡しになるのです。
オブジェクトが値渡しされるなら、C/C++のようにオブジェクトの実体がコピーされるはずですが、そのようなことはおこりません。

Javaでは、オブジェクトはすべてHeapメモリにアロケートされ、C/C++とは異なり評価スタック上に作られることはありません。メソッド引数を渡すのにスタック上にオブジェクトの実体の代わりに、オブジェクトの参照が積まれます。C++の比喩で言えば、オブジェクト参照はすべてObject *objのようなポインタが値渡しで渡され、メソッド実行はobj->method()のように実行されます。ただ、Javaの表記上はobj.method()のように書くだけです。

次のプログラムを見てください。arg= nullの結果は、main()のmbに影響を与えないはずです。参照渡しならmainの変数mbがnullになるはずです。Javaコードと等価なC++風のコードも付けておきます。&をつかっていないでしょ。だから参照渡しではないのです。



[Java]
public class Btest{
  public static void main(String[] args) {
    MyBoolean mb = new MyBoolean();
    setFalse(mb);
    System.out.println(mb); // nullにはならない
  }
  static void setFalse(MyBoolean arg) { 
    arg= null;
  }
}

[C++]
public class Btest{
  public static void main(String[] args) {
    MyBoolean_impl *mb = new MyBoolean_impl();
    setFalse(mb);
    printf("%x", mb); // NULLにはならない
  }
  static void setFalse(MyBoolean_impl *arg) {   
    arg = NULL;
  }
}


[ メッセージ編集済み 編集者: miki 編集日時 2002-03-15 17:49 ]

投稿者:miki

編集 履歴 (0)

「参照渡し」の定義が人によってまちまちなのが混乱の原因だと思います。
「参照を渡す」のと「参照渡し」は全然意味が違います。
私の定義は「プログラミング言語Java第3版」の引用に書いた通りです。
この定義はC++とも同じだと思います。
[ メッセージ編集済み 編集者: miki 編集日時 2002-03-15 18:05 ]

投稿者:miki

編集 履歴 (0)

mikiさん
とてもお詳しいですね。
「オブジェクト参照が値渡しされる」とおっしゃる意味がよく分かりました。
どうもありがとうございました。

投稿者:katsum

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。