Thursday, July 12, 2007

Parameter Passing in Java

A couple of days ago, I felt like refreshing on refactoring knowledge by digging up my copy of Martin Fowler’s Refactoring: Improving the Design of Existing Code.

Once I reached Remove Assignments to Parameters (131) I came across past notes of mine, in color :) - which usually is a good indicator for raised attention levels.

The refactoring item elaborates on the bug-inviting practice of reassigning values to parameters.

Upon re-reading the refactoring and my notes, I remembered that prior to initially working through the book years ago, I thought that in Java primitive parameters were passed by value whereas objects were passed by reference.

Even though the item points out that this isn’t so - all parameters are passed by value - I would have liked to see a little more light shed on the issue.

At first thought, the Java language’s strict adherence to call-by-value parameter passing conflicts with the notion of being able to modify passed objects.

In contrast to primitives, objects do not live on the call stack. They reside on the heap instead and outlive method execution. Object states can be modified from within methods. Giving in to intuition this sounds much more like call-by-reference handling of non-primitive parameters.

It is assignments to parameters within methods, that proves intuition wrong.

A tiny example is to serve to illustrate the issue. Class Foo is trivial, just holds a private int instance variable, initialized via its constructor, and provides getter and setter methods.

class CallByValueGoodness {

private static void incrementBar (Foo aFoo) {
aFoo.setBar( aFoo.getBar() + 1 );
}

private static void incrementReassignedBar (Foo anotherFoo) {
anotherFoo = new Foo( anotherFoo.getBar() + 1 );
}

public static void main (String[] args) {
Foo firstObj = new Foo(5);
Foo secondObj = new Foo(5);
incrementBar(firstObj);
incrementReassignedBar(secondObj);
System.out.println(firstObj.getBar());
System.out.println(secondObj.getBar());
}
}
Running the main-method will output:

6
5
Even though both Foo-objects have seemingly been updated, secondObj does not reflect any changes.
This is due to Java’s parameter passing by value.

When calling a method with a non-primitive parameter a new reference, i.e. a copied reference, is passed instead of the original reference.

Any reassignments to the parameter reference, that is anotherFoo in the example, do not influence the caller’s reference to the object.