views:

143

answers:

3

I thought when you passed objects to methods in Java, they were supposed to be by value.

Here is my code:

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];
            }
        }
    }

At this point, any changes to the mTiles object are reflected back to the tiles object. Any ideas how to fix this?

Thanks all.

+7  A: 

It is by val, but when talking about objects, what gets passed is the value of the reference, so, if your object is mutable ( as in your example ) modifying the object through the reference copy, will modify the underlaying object.

To solve it, you have to copy your object and pass that new reference value.

In your code, you are using the same reference value in "tiles" and in "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

You would have to create a new one like this:

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

Or

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

Or

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

Or something like that, so you can create actually a new one, instead of using the same ref.

I hope this helps.

EDIT

When you change the ref in pass-by-val, the original is not affected, ie:

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

After this, the original "hi" is still "hi". In pass-by-ref it would be "bye"

Here some links:

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

http://stackoverflow.com/questions/514478/can-someone-explain-to-me-what-the-reasoning-behind-passing-by-value-and-not-by

http://stackoverflow.com/questions/2027/pass-by-reference-or-pass-by-value/7485

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

An ultimately:

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

OscarRyz
I'm not sure I understand. What is the difference between that and pass-by-ref.
Cool, thank you. I believe I've grasped the concept, however, none of your solutions work...probably because my class object hasn't implemented any of those methods. I did try overloading the Tile constructor, to use your first suggestion but without success. I am immensely frustrated that this is so difficult. Is the clone method easy to add to my class? Then I'm going to struggle with deep vs shallow.../sigh.
+1  A: 

Here are some diagnostic examples for Java's argument passing semantics:

For an object type:

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"

For a primitive type:

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"

In fact, in both of those examples, the assignments within the changeIt methods only affect the respective method's local variable, and the actual values printed will be "hi" and "0".

EDIT

And since the OP still doesn't believe me ... here's a diagnostic example to show that Java is call-by-value for mutable objects as well.

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"

By contrast, this example will give the same answer for both call by reference and call by value semantics. It doesn't prove anything about argument passing semantics.

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

Trust me, I've been programming Java for > 10 years, and I've taught comparative programming language courses at university level.

Stephen C
Thanks. Based on the other answer, it seems that objects behave differently.
Actually, (real) Java's argument passing semantics are **the same** for objects and primitive types, and my example code clearly demonstrates this. Furthermore, the other answer does not state that objects and primitive types behave differently. And if you think it implies that, then you are clearly not understanding what it is saying.
Stephen C
Strings are immutable thus behaving differently than other mutable objects. I don't think your example applies to mutable objects.
I'd really like to believe you cause it would make my life a whole lot easier, I wouldn't be in the position I am with my program. Please feel free to help me correct my program as it doesn't work they the way you say it should. I'm pretty sure the other guy is correct in this regard, meaning I'm not exactly passing the value, I'm passing the value of the reference.
The Oscar's answer is correct, and so am I. The problem is that **you** still don't understand how we can both be correct. And no, I'm not going to write your code for you. BTW, Oscar's suggested solutions to the problem are all sound, but are not copy-and-paste answers.
Stephen C
A: 

You don't pass the object, you pass a references to the object, which is copied by value.

Steve Kuo