0

Javaのメソッドにオブジェクトを渡すとき、それらは値によるものであるはずだと思いました。

これが私のコードです:

public class MyClass{
    int mRows;
    int mCols;
    Tile mTiles[][];     //Custom class

    //Constructor
    public MyClass(Tile[][] tiles, int rows, int cols) {
        mRows = rows;
        mCols = cols;

        mTiles = new Tile[mRows][mCols];
        for (int i=0; i < mRows; i++) {
            for (int j=0; j < mCols; j++) {
                mTiles[i][j] = new Tile();
                mTiles[i][j] = tiles[i][j];
            }
        }
    }

この時点で、オブジェクトへの変更はすべてmTilesタイルオブジェクトに反映されます。これを修正する方法はありますか?

皆さんありがとう。

4

4 に答える 4

7

これはvalによるものですが、オブジェクトについて話すとき、渡されるのは参照の値です。したがって、オブジェクトが変更可能である場合(例のように)、参照コピーを介してオブジェクトを変更すると、下にあるオブジェクトが変更されます。

これを解決するには、オブジェクトをコピーして、その新しい参照値を渡す必要があります。

コードでは、「タイル」と「mTiles」で同じ参照値を使用しています

mTiles[i][j] = new Tile(); // <---this line is useless by the way
mTiles[i][j] = tiles[i][j] // because you then assign the same value here

次のような新しいものを作成する必要があります。

mTiles[i][j] = new Tile(tiles[i][j]);

または

mTiles[i][j] = tiles[i][j].clone();

または

mTiles[i][j] = Tile.newFrom( tiles[i][j] );

またはそのようなものなので、同じ参照を使用する代わりに、実際に新しいものを作成できます。

これがお役に立てば幸いです。

編集

pass-by-valで参照を変更しても、元の参照は影響を受けません。つまり、次のようになります。

String s = "hi"; 
changeIt(s);
....
void changeIt(String s){ 
    s = "bye" // assigning to the copy a new ref value of "bye"
}

この後、元の「こんにちは」はまだ「こんにちは」です。pass-by-refでは「さようなら」になります

ここにいくつかのリンクがあります:

http://www.javaranch.com/campfire/StoryPassBy.jsp

Javaで「参照」ではなく「値」を渡す理由を誰かに説明してもらえますか?

参照渡しですか、それとも値渡しですか?

http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html

http://academic.regis.edu/dbahr/GeneralPages/IntroToProgramming/JavaPassByValue.htm

http://www.jguru.com/faq/view.jsp?EID=430996

最終的に:

http://www.google.com.mx/search?sourceid=chrome&ie=UTF-8&q=java+is+pass+by+value

于 2010-09-29T23:03:28.763 に答える
1

以下に、Java の引数受け渡しセマンティクスの診断例をいくつか示します。

オブジェクト タイプの場合:

void changeIt(String s) {
    s = "bye";
}

String s = "hi"; 
changeIt(s);
System.out.println(s);  // would print "bye" for call by reference
                        // but actually prints "hi"

プリミティブ型の場合:

void changeIt(int i) {
    i = 42;
}

int i = 0; 
changeIt(i);
System.out.println(i);  // would print "42" for call by reference
                        // but actually prints "0"

実際、これらの例の両方で、メソッド内の割り当てchangeItはそれぞれのメソッドのローカル変数にのみ影響し、出力される実際の値は「hi」と「0」になります。

編集

そして、OPはまだ私を信じていないので... Javaが可変オブジェクトに対しても値渡しであることを示す診断例を次に示します。

public class Mutable {
    int field;
    public Mutable(int field) { this.field = field; }
    public void setField(int field) { this.field = field; }
    public int getField() { return field; }
}

void changeIt(Mutable m, Mutable m2) {
    m = m2;  // or 'm = new Mutable(42);' or 'm = null;'
}

Mutable m = new Mutable(0); 
Mutable m2 = new Mutable(42); 
changeIt(m, m2);
System.out.println(m.getField());  
                        // would print "42" for call by reference
                        // but actually prints "0"

対照的に、この例では、参照による呼び出しと値による呼び出しの両方のセマンティクスに対して同じ答えが得られます。引数を渡すセマンティクスについては何も証明していません。

void changeIt2(Mutable m) {
    m.setField(42);
}

Mutable m = new Mutable(); 
changeIt2(m);
System.out.println(m.getField());  
                        // prints "42" for both call-by reference
                        // and call-by-value

私は 10 年以上 Java をプログラミングしており、大学レベルで比較プログラミング言語コースを教えてきました。

于 2010-09-29T23:30:48.133 に答える
1

オブジェクトを渡すのではなく、値によってコピーされるオブジェクトへの参照を渡します。

于 2010-09-30T00:23:51.900 に答える
0

実際のところ、Java は値渡しです。ただし、「参照による配列」もあります。そのため、Java はオブジェクト (少なくとも配列) の場合は参照渡しであり、プリミティブの場合は値渡しのみであると多くの人が考えています。

ここに短いテストがあります:

String[] array = new String[10];
array[0] = "111";
ArrayList one = new ArrayList(); 
one.add(array);
ArrayList two = (ArrayList) one.clone(); //Alternate with: ArrayList two = one;
String[] stringarray1 = (String[]) one.get(0);
String[] stringarray2 = (String[]) two.get(0);
System.out.println("Array: "+one+" with value: "+stringarray1[0]);
System.out.println("Array: "+one+" with value: "+stringarray2[0]);
array[0] = "999";
String[] stringarray3 = (String[]) one.get(0);
String[] stringarray4 = (String[]) two.get(0);
System.out.println("Array: "+one+" with value: "+stringarray3[0]);
System.out.println("Array: "+two+" with value: "+stringarray4[0]);

複製するか = を使用するかに関係なく、System.out.print は常に次のようになります。

Array: [[Ljava.lang.String;@addbf1] with value: 111
Array: [[Ljava.lang.String;@addbf1] with value: 111
Array: [[Ljava.lang.String;@addbf1] with value: 999
Array: [[Ljava.lang.String;@addbf1] with value: 999

これは、クローンと配列が有害な組み合わせであることを証明しています。なぜなら、配列はポインタしか格納しないからです! これが配列のないオブジェクトにも当てはまるかどうかをテストする必要があります...これは、Javaが常に「参照によるストレージ」であることを意味するためです(そして、「クローン」機能は、オブジェクトにとって悪い冗談にすぎません)プリミティブのみが実際の値であり、参照はありません!

そして、ロジックについて知っているので: storage-by-reference x pass-by-value == "storage-by-value x pass-by-reference" (オブジェクトの場合!)、

学校からすでに知っていましたが、値によるストレージ x 値による受け渡し (プリミティブの場合)

では、私たちは皆、プログラミングの教師に (大学でも) 嘘をつかれたのでしょうか? たぶん、しかし彼らは少なくとも論理的な誤りを犯していませんでした...だからそれは嘘ではなく、ただ間違っていました.

編集

上記と同じコードをクラスで書きました。最初にデータ構造です。

public class Foobar implements Cloneable {
    String[] array;

    public Foobar() {
        this.array = new String[10];
    }

    public String getValue(){
        return array[0];
    }

    public String[] getArray(){
        return array;
    }

    public void setArray(String[] array){
        this.array = array;
    }

    @Override
    public Object clone(){
        try{
            Foobar foobar = (Foobar) super.clone();
            foobar.setArray(array);
            return foobar;
        }
        catch(Exception e){
            return null;
        }
    }
}

今コントローラー:

String[] array = new String[10];
array[0] = "111";
Foobar foo1 = new Foobar();  
foo1.setArray(array);
Foobar foo2 = foo1; //Alternation: Foobar foo2 = (Foobar) foo1.clone();  
System.out.println("Instance: "+foo1.getArray()+" with value: "+foo1.getValue());
System.out.println("Instance: "+foo2.getArray()+" with value: "+foo2.getValue());
array[0] = "999";
System.out.println("Instance: "+foo1.getArray()+" with value: "+foo1.getValue());
System.out.println("Instance: "+foo2.getArray()+" with value: "+foo2.getValue());

= または clone() を使用しても、テスト結果は常に次のようになります。

Instance: [Ljava.lang.String;@42e816 with value: 111
Instance: [Ljava.lang.String;@42e816 with value: 111
Instance: [Ljava.lang.String;@42e816 with value: 999
Instance: [Ljava.lang.String;@42e816 with value: 999

これで、すべてのオブジェクトをすぐに支配できる「マスター アレイ」をポケットに入れました。(これは本当に良いことではありません)

私はいつも Java 配列に不安を感じていましたが、それが何であるかは言えませんでした。それ以来、配列はオブジェクトのコンテナーとしてしか使用していなかったので、気分が良くなりました... PHP などのスクリプト言語での配列の重要性に非常に驚いています。

それでも、Java 配列は、簡単に渡して共有値にアクセスできるため、スレッド間の同期に最適です。しかし、PHP や C++ などの出身のプログラマーは、実際に Java 配列でいくつかの問題を経験する可能性があります。;D

ああ、私はこの記事が好きです: http://javadude.com/articles/passbyvalue.htm

更新:配列を含むオブジェクトをコピーするための優れたソリューションを見つけました。ここで私の解説を参照してください: Object.clone() の使用におけるバグ

于 2012-10-20T16:46:06.397 に答える