2

I am fairly new to Java, and recently I was reading some material about Java being pass-by-value. I've read over this question, and this blog before running a test myself.

Now, based on my reading and my quick test, I found that there are two ways that I can alter the variables contained within an object reference. Which of the below approaches is the better or safer approach? Are there any obvious issues with either approach?

Both of these print out "iArr[0] = 45".

Approach 1:

public static void main(String args[] ){
   int[] iArr = {1};
   method(iArr) ;
   System.out.println( "iArr[0] = " + iArr [0] ) ;
}
public static void method(int[] n ) { 
    n [0] = 45 ;
}

Approach 2:

public static void main(String args[] )
{
   int[] iArr = {1};
   iArr = method(iArr) ;
   System.out.println( "iArr[0] = " + iArr [0] ) ;
}
public static int[] method(int[] n ) { 
    n [0] = 45 ;
    return n;
}
Community
  • 1
  • 1
etech
  • 2,548
  • 1
  • 27
  • 24
  • I find *neither* approach ideal as they both cause side-effects. I actually **really dislike** approach #2 because it "hides" this fact, while approach #1 "does it's dirty job", but I can tell that quickly from the `void` return type. (I do not consider is "being chainable" enough to counteract that.) –  Jun 21 '12 at 23:19
  • "best practice for modifying object reference values in java?" Best practice is don't. – xxpor Jun 21 '12 at 23:20
  • The whole thing about pass by value/reference drives me nuts. Java just does what you usually expect and always should do, period. You might examine what pass by value does and you might be able to come up with some neat tricks around it but they will generally lead to harder to read code and only be useful in the edgiest of edge-cases. Always just use parameters for INs, "return" for OUTs and an object you pass IN might have it's state modified. If you are modifying the actual VALUE of your callers parameter you are going to piss off anyone tryign to reuse or fix your code later. – Bill K Jun 21 '12 at 23:21

2 Answers2

2

The second approach open up the possibility of aliases.

int[] n = {1};
int[] j;

j = method(n);
j[0] = 342;
System.out.println("iArr[0] = " + n[0]);
System.out.println("iArr[0] = " + j[0]);

Will print out:

iArr[0] = 342
iArr[0] = 342

Because of this I would choose the first approach in this situation. You only want to change the array, there is no need to return the reference. It is also easy to create your own alias, if you need it. It is also not clear from the second approach that you change the actual parameter values, which I think is very bad practice.

MAV
  • 7,260
  • 4
  • 30
  • 47
2

I find neither approach ideal as they both cause the same side-effects.

That is, they are the same but the 2nd approach also returns the modified object: the 2nd approach still modifies the array object passed in! The re-assignment to iArr of the return value from #2 in the example code has no effect on the object modified! Remember that Java uses Call-By-Object-Sharing semantics (for reference types); the return value is unrelated to this behavior.

I actually really dislike approach #2 because it "hides" this fact (I look at the signature and think "oh, I get a new array object!"), while approach #1 "does it's dirty job", but I can tell that quickly from the void return type. (In some advanced casing "chaining" can be useful; this is not one of them.)

Here is a trivial version which does not cause side-effects: (I would suggest minimizing side-effects in general as it often makes code easier to reason about and debug.)

public static void main(String args[] )
{
   int[] iArr = {1};
   int[] newArr = method(iArr) ;
   System.out.println( "iArr[0] = " + iArr [0] ) ;
   // This is different here, but it would "be the same" in the 
   // 2nd-case example in the post.
   System.out.println( "newArr[0] = " + newArr [0] ) ;
}
public static int[] method(int[] n ) {
    // This is a silly stub method, n would be presumably used.
    int[] x = new int[1];
    x[0] = 45; // modifying a DIFFERENT object
    return x;  // returning the NEW object
}
  • Thanks. This is how I usually would perform an operation.. create a new object. I was surprised and somewhat startled that my approach 1 was even possible. However, I suppose one 'benefit' of approach 1 would be that it saves memory by not creating a new object (and doesn't manipulate the stack as Evan mentioned). That said, I don't think that manipulating an object's values in place like approach one is appropriate for public methods (maybe just for internal, private methods). – etech Jun 21 '12 at 23:52
  • It is *very important* to understand Call-By-Object-Sharing semantics. In a nutshell it means that reference-type values (or "objects") are **not** copied/cloned/duplicated when passed as an argument or assigned to a variable. Also note again, **approach #1 and approach #2 are the same**; returning the object is *unrelated* to mutating the object. At best it is a bad example; however I suspect it is confusing/misleading. See MAV's answer. –  Jun 22 '12 at 00:07
  • Marking this as the answer because you point out more of what is wrong with approach #2, and also brought up Call By Object Sharing. Thanks! – etech Jun 22 '12 at 02:37