この問題は何度も対処されていることは知っていますが、私の Java/C++ の知識は非常に弱く、答えをほとんど理解できません :-( ... 私が本当に望んでいるのは、非常に単純な例です。
C++ では、次のように記述できます。
void func()
{
int x = 3;
add_one(x);
// now x is 4.
}
void add_one(int &var)
{
var++;
}
私が今見たいのは、Java で同じ効果を達成する最も簡単な方法です。
この問題は何度も対処されていることは知っていますが、私の Java/C++ の知識は非常に弱く、答えをほとんど理解できません :-( ... 私が本当に望んでいるのは、非常に単純な例です。
C++ では、次のように記述できます。
void func()
{
int x = 3;
add_one(x);
// now x is 4.
}
void add_one(int &var)
{
var++;
}
私が今見たいのは、Java で同じ効果を達成する最も簡単な方法です。
直接できません。あなたが得ることができる最も近い方法は、値をオブジェクトに入れ、参照を(値によって、参照がコピーされるように)メソッドに渡すことです。
void func()
{
int x = 3;
int[] holder = [x];
add_one(holder);
// now holder[0] is 4. x is still 3.
}
// container here is a copy of the reference holder in the calling scope.
// both container and holder point to the same underlying array object
void add_one(int[] container)
{
container[0]++;
}
ここでは配列を使用していますが、ラッパーは任意のオブジェクトにすることができます。
Java メソッドの引数は値渡しであり、関数内で変更することはできません。オブジェクトまたは配列で int (またはその他のプリミティブ型) をラップする必要があります。オブジェクトまたは配列をメソッド引数として渡すと、オブジェクトの変更に使用できる参照が渡されます。
Java には、プリミティブ型 ( Integerなど) 用の Object ベースのラッパーが既にありますが、これらは設計上不変です。一部のライブラリは、これらのラッパーの変更可能なバージョンを提供します。独自のものを作成することもできます:
public class MutableInt
{
private int val;
public MutableInt(int val)
{
this.val = val;
}
public void setValue(int newVal)
{
this.val = newVal;
}
public int getValue()
{
return this.val;
}
}
void func()
{
int x = 3;
MutableInt wrapper = new MutableInt(x);
add_one(wrapper);
}
void add_one(MutableInt arg)
{
arg.setValue(arg.getValue() + 1);
}
これはできません。Javaは値渡しのみです。プリミティブは明らかですが、オブジェクトに渡されるのは参照であり、オブジェクト自体ではありません。
他の回答からわかるように、Javaは純粋に値渡しです。オブジェクトは、「値参照」と呼ばれるものによって渡されます。Javaのオブジェクトは単なるポインタ参照であるため、「値」はオブジェクトがヒープ上に存在するアドレスと考えることができます。したがって、メソッド呼び出しを行うときは、「値」、別名アドレスをメソッドパラメーターにコピーします。
Object x = new Object();
foo(x);
ヒープ->オブジェクトの割り当て(5000)
スタック->ローカル変数の割り当て(1000)
スタックアドレス1000を5000に設定(オブジェクトインスタンスを指すため)
したがって、ここには2つの別々のメモリ割り当てがあることがわかります。変数の「値」は、ヒープ上のアドレスと見なされます。
スタック->メソッドパラメータ8000の割り当て
スタックアドレス8000は、渡されたパラメータ5000と同じ値に設定されます
これが、メソッドでオブジェクトインスタンスを再割り当てした場合、それが呼び出し元に伝播されない理由です。スタック位置8000のヒープ位置を変更したはずです。また、呼び出し元のメソッドのスタック位置1000の値は5000(元のオブジェクトインスタンス)のままです。
Cでは次のように考えてください。
void method(myobject * obj);
あなたは確かに"obj"のフィールドを変更することができます、そしてあなたはこれをローカルで行うことができます:
obj = new myobject();
ただし、呼び出し元には、渡された元の値が引き続き表示されます。
Javaには、&reference演算子に類似したものはありません。
そして、あなたの目的のために使用できる組み込みのクラスがあります。AtomicInteger、AtomicLongなどは変更可能ですが、同期が関係しているためにパフォーマンスが低下する可能性があります。
参照によるパスをシミュレートするすべての状況を説明するために、汎用のValueHolderクラスをお勧めします。
public class ValueHolder<T> {
private T value;
// getter/setter/constructor
}
public int getOneMore(int val) {
return val + 1;
}
Java では、オブジェクトの参照によるコピーと、プリミティブ型 (int、float など) の vlaue によるコピーが可能です。これはデフォルトであり、変更されることはありません。関数内の int の値を変更する必要がある場合は、たとえばクラス Integer を使用できます