0

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.

DonGru
  • 13,532
  • 8
  • 45
  • 55
user432209
  • 20,007
  • 10
  • 56
  • 75
  • 2
    Already asked: http://stackoverflow.com/questions/514478/can-someone-explain-to-me-what-the-reasoning-behind-passing-by-value-and-not-by – rkg Sep 29 '10 at 23:06

4 Answers4

7

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

Can someone explain to me what the reasoning behind passing by "value" and not by "reference" in Java is?

Pass by reference or pass by value?

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&ie=UTF-8&q=java+is+pass+by+value

Community
  • 1
  • 1
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • I'm not sure I understand. What is the difference between that and pass-by-ref. – user432209 Sep 29 '10 at 23:07
  • 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. – user432209 Sep 30 '10 at 00:28
1

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
  • 698,415
  • 94
  • 811
  • 1,216
  • Thanks. Based on the other answer, it seems that objects behave differently. – user432209 Sep 30 '10 at 00:36
  • 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 Sep 30 '10 at 00:45
  • Strings are immutable thus behaving differently than other mutable objects. I don't think your example applies to mutable objects. – user432209 Sep 30 '10 at 01:54
  • 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. – user432209 Sep 30 '10 at 22:48
  • 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 Oct 01 '10 at 00:23
1

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

Steve Kuo
  • 61,876
  • 75
  • 195
  • 257
0

As a matter of fact, Java is pass-by-value. But it also has "arrays-by-reference"! That's why many people think Java is pass-by-reference for objects (at least for arrays) and only pass-by-values for primitives.

Here is a short test:

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

No matter if you clone or use =, the System.out.print will always look like this:

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

This proves that cloning and arrays are a harmful combination, because arrays only store pointers! I still need to test if this is also true for no-array objects...because this would mean that Java is always "storage-by-reference" (and the "clone"-function would be only a bad joke for any objects containing arrays), while only primitives are real values and no references!

And since we know about logic: storage-by-reference x pass-by-value == "storage by value x pass-by-reference" (for objects!),

while we already knew since school: storage-by-value x pass-by-value (for primitives)

So, were we all lied to by our programming teachers (even at university)? Maybe, but they didn't make any logical errors at least...so it wasn't a lie, it was just wrong.

EDIT

I wrote the same code as above with a class, first the data structure:

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

Now the controller:

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());

The test results will always look like that - no matter if I use = or 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

Now I've got the "master array" in the pocket, with whom I can rule all objects right away! (which isn't really a good thing)

I always felt uneasy about Java arrays, but I couldn't say what it was. Now I know it, and I feel good since I only used arrays as container for objects ever since...only to find myself being quite surprised how important they are in script languages like PHP!

Still, Java arrays are great for synchronisation between threads, as you can pass them easily and still access shared values. But programmers coming from PHP or C++ or somewhere else may indeed experience some problems with Java arrays. ;D

Oh, I like this article: http://javadude.com/articles/passbyvalue.htm

UPDATE: I've found a great solution to copy any object containing arrays, see my commentary here: Bug in using Object.clone()

Community
  • 1
  • 1
mark
  • 84
  • 1
  • 2