Question:
I know that in order to pass something byreference, you pass an already-created objectand let the subroutine modify the contents ofthat object. Unfortunately, the Integer
, String
,etc. objects don’t allow their internal values tobe changed. How can I do the equivalent ofvoid myRoutine (int *x, int *y, int *z
) in Java?
Answer:
First a little background for Java newcomers:
Java methods can manipulate two types of parameters: scalars (numbers,booleans and characters) and pointers to objects (objects = arrays orclass instances). A method receives private copies of all its parameters.In the case of a pointer, this amounts to a private or local copy of apointer to a non-local object, which means modifications made inside amethod to instance variables or components of an object pointer parameterare modifications made to the corresponding non-local object. Thesemodifications can be intentional or unintentional.
The Java parameter passing mechanism can be compared to the pass-by-valueand pass-by-reference mechanisms in C++:
It’s also similar to passing pointers in C (or C++) except pointer parametersmust be explicitly dereferenced in the function body, and addresses must beexplicitly passed as inputs:void foo(Object x, scalar y) { … } // Java method void foo(Object &x, scalar y) { … } // corresponding C++ function
When would a programmer intentionally modify instance variables of a non-localobject? This would be the case if the object models a mutable “real world”object, i.e. an object with internal state that can change without changingthe identity of the object. The classical example is a bank account. In thiscase its mutable internal state is its balance:void foo(Object *x, scalar y){ … } // bind x to &a, use *x in …
Here’s a fragment of code that manipulates bank accountbalances:class Account { // … public double balance; // should be private, see note at bottom }
A method that intentionally modifies an instance variable of a non-localobject is called a mutator.class Bank { private Account[] accts = new Account[500]; // the bank’s accounts // … public void deposit(double amount, Account acct) { acct.balance += amount; // will modify non-local accts[i] } // … }
Enough background, back to the original question. For every Java scalartype there is a corresponding Java class called the wrapper for that type.For example, the wrapper for the type int
is the classInteger. Thus, everyscalar has a corresponding class instance. This gives us a choice when wedesign a method that expects a scalar input.
Should the input be a scalaror a pointer to the corresponding object wrapper? For example:
In the first case,void foo(int x) { … } or void foo(Integer x) { … }?
foo
will get private copies of itsint
parameters; inthe second case, foo
receives pointers to its Integerparameters. Is there a danger in the second case that the non-local Integerobject willbe unintentionally modified inside foo()
? This might be aconcern, except Java regards scalars as non-mutable (i.e. stateless)objects. Indeed,this is the view most mathematicians have of scalars. (C-isms like”x = x + 1
” drive mathematicians crazy!) Since scalars areregarded asnon-mutable, Java does not provide any way for programmers to modify theinternal state, i.e. the value, of an Integer object since this would beequivalent to trying to reassign the value of a constant: 5 = 5 + 1.
Nevertheless, C and C++ programmers often pass scalar pointers andscalar references to functions that modify theirparameters:
How would this be done in Java?void inc(int *x) { *x = *x + 1;} or voidinc(int &x) { x = x + 1; }
First, notice that neither function makes sense if called with a constant:
(This is handled by passing a temporary variable containing 5.)inc(5)
The input must be a variable or a pointer to one. That being thecase, we should think of the true input to inc
as a mutablecontainer. With this in mind, we should avoid passing non-mutable Integerwrappers to mutators, and instead pass custom made mutable integercontainers:
Here’s inc:class IntContainer { // … public int value; }
One last note: It’s bad form to have public instance variables. Instead,provide member function mutators and accessors:void inc(IntContainer x) { x.value = x.value + 1; }
and then:class IntContainer { // … // accessor (aka selector) public int getValue() { return value; } // mutator public void setValue(int val) { value = val; } // … private int value; }
void inc(IntContainer x) { x.setValue(x.getValue() + 1); }