How can I pass an Integer class correctly by reference?
Asked Answered
C

11

89

I am hoping that someone can clarify what is happening here for me. I dug around in the integer class for a bit but because integer is overriding the + operator I could not figure out what was going wrong. My problem is with this line:

Integer i = 0;
i = i + 1;  // ← I think that this is somehow creating a new object!

Here is my reasoning: I know that java is pass by value (or pass by value of reference), so I think that in the following example the integer object should be incremented each time.

public class PassByReference {

    public static Integer inc(Integer i) {
        i = i+1;    // I think that this must be **sneakally** creating a new integer...  
        System.out.println("Inc: "+i);
        return i;
    }

    public static void main(String[] args) {
        Integer integer = new Integer(0);
        for (int i =0; i<10; i++){
            inc(integer);
            System.out.println("main: "+integer);
        }
    }
}

This is my expected output:

Inc: 1
main: 1
Inc: 2
main: 2
Inc: 3
main: 3
Inc: 4
main: 4
Inc: 5
main: 5
Inc: 6
main: 6
...

This is the actual output.

Inc: 1
main: 0
Inc: 1
main: 0
Inc: 1
main: 0
...

Why is it behaving like this?

Condominium answered 25/7, 2010 at 20:24 Comment(1)
Related question: #4520637Prostration
C
81

There are two problems:

  1. Integer is pass by value, not by reference. Changing the reference inside a method won't be reflected into the passed-in reference in the calling method.
  2. Integer is immutable. There's no such method like Integer#set(i). You could otherwise just make use of it.

To get it to work, you need to reassign the return value of the inc() method.

integer = inc(integer);

To learn a bit more about passing by value, here's another example:

public static void main(String... args) {
    String[] strings = new String[] { "foo", "bar" };
    changeReference(strings);
    System.out.println(Arrays.toString(strings)); // still [foo, bar]
    changeValue(strings);
    System.out.println(Arrays.toString(strings)); // [foo, foo]
}
public static void changeReference(String[] strings) {
    strings = new String[] { "foo", "foo" };
}
public static void changeValue(String[] strings) {
    strings[1] = "foo";
}
Clapp answered 25/7, 2010 at 20:30 Comment(5)
It's not really pass by value. It's pass by value of the reference.... as the only thing being passed for objects is a memory address.Simulator
Strictly speaking, for references it's "pass reference by value" and for primitives it's just "pass by value".Clapp
well, a duplicate of reference is sent into the functionAyr
How is duplicate of reference sent to function different from sending reference to function? Is there any significant difference?Brunson
BalusC has nailed it, but here is simple explanation. Java sends in a copy-of-reference which is created on a function's stack.The line strings = new String[]{"foo", "foo"}; modifies the copy-of-reference created on changeReference(String[]) local stack. Calling function main() is aware of reference created on it's own stack (not that of created on changeReference() stack).Teeth
E
31

Good answers above explaining the actual question from the OP.

If anyone needs to pass around a number that needs to be globally updated, use the AtomicInteger() instead of creating the various wrapper classes suggested or relying on 3rd party libs.

The AtomicInteger() is of course mostly used for thread safe access but if the performance hit is no issue, why not use this built-in class. The added bonus is of course the obvious thread safety.

import java.util.concurrent.atomic.AtomicInteger
Eddington answered 26/2, 2015 at 9:42 Comment(3)
What about performance, is it intact? Obviously if AtomicInteger is needed only to pass by reference it isn't needed for thread safety.Thermoelectrometer
Performance seems to be the same as Integer (assuming you are just using .set(), .get() and are not using any of the thread-safe operations).Unhand
this is real solution, should be at topFortdefrance
W
27

The Integer is immutable. You can wrap int in your custom wrapper class.

class WrapInt{
    int value;
}

WrapInt theInt = new WrapInt();

inc(theInt);
System.out.println("main: "+theInt.value);
Whose answered 25/7, 2010 at 20:35 Comment(4)
Why Integer is made immutable?Crave
@RahulJain Not sure, I suspect it is to be consistent with the actual value type (int), so they both have the same semantics.Whose
What do you mean by consistent. int can be changed but not Integer, This is kinda downside of the wrapper class.Crave
@RahulJain int is a primitive type. Primitive types are immutable. I suspect you confuse immutability with the final keyword - they are different things. You can change the reference of Integer variable (the same as the value of int variable). They are pretty much consistent.Whose
P
19

There are 2 ways to pass by reference

  1. Use org.apache.commons.lang.mutable.MutableInt from Apache Commons library.
  2. Create custom class as shown below

Here's a sample code to do it:

public class Test {
    public static void main(String args[]) {
        Integer a = new Integer(1);
        Integer b = a;
        Test.modify(a);
        System.out.println(a);
        System.out.println(b);

        IntegerObj ao = new IntegerObj(1);
        IntegerObj bo = ao;
        Test.modify(ao);
        System.out.println(ao.value);
        System.out.println(bo.value);
    }


    static void modify(Integer x) {
        x=7;
    }
    static void modify(IntegerObj x) {
        x.value=7;
    }   
}

class IntegerObj {
    int value;
    IntegerObj(int val) {
        this.value = val;
    }
}

Output:

1
1
7
7
Powerful answered 11/10, 2014 at 14:41 Comment(0)
D
17

What you are seeing here is not an overloaded + oparator, but autoboxing behaviour. The Integer class is immutable and your code:

Integer i = 0;
i = i + 1;  

is seen by the compiler (after the autoboxing) as:

Integer i = Integer.valueOf(0);
i = Integer.valueOf(i.intValue() + 1);  

so you are correct in your conclusion that the Integer instance is changed, but not sneakily - it is consistent with the Java language definition :-)

Donielle answered 25/7, 2010 at 20:35 Comment(0)
E
7

You are correct here:

Integer i = 0;
i = i + 1;  // <- I think that this is somehow creating a new object!

First: Integer is immutable.

Second: the Integer class is not overriding the + operator, there is autounboxing and autoboxing involved at that line (In older versions of Java you would get an error on the above line).
When you write i + 1 the compiler first converts the Integer to an (primitive) int for performing the addition: autounboxing. Next, doing i = <some int> the compiler converts from int to an (new) Integer: autoboxing.
So + is actually being applied to primitive ints.

Essayistic answered 25/7, 2010 at 20:44 Comment(0)
S
3

I think it is the autoboxing that is throwing you off.

This part of your code:

   public static Integer inc(Integer i) {
        i = i+1;    // I think that this must be **sneakally** creating a new integer...  
        System.out.println("Inc: "+i);
        return i;
    }

Really boils down to code that looks like:

  public static Integer inc(Integer i) {
        i = new Integer(i) + new Integer(1);      
        System.out.println("Inc: "+i);
        return i;
    }

Which of course.. will not changes the reference passed in.

You could fix it with something like this

  public static void main(String[] args) {
        Integer integer = new Integer(0);
        for (int i =0; i<10; i++){
            integer = inc(integer);
            System.out.println("main: "+integer);
        }
    }
Simulator answered 25/7, 2010 at 20:31 Comment(2)
huh, ok that is interesting. Thanks for the auto-boxing term. Immutable also seems pretty key. Have a good weekend!Condominium
Ya look up autoboxing. Back in Java 1.4 you could only add ints with ints, or Integers with Integers. The fact you can do in any way now can be a little difficult to conceptualize just looking at code.Simulator
R
3

If you change your inc() function to this

 public static Integer inc(Integer i) {
      Integer iParam = i;
      i = i+1;    // I think that this must be **sneakally** creating a new integer...  
      System.out.println(i == iParam);
      return i;
  }

then you will see that it always prints "false". That means that the addition creates a new instance of Integer and stores it in the local variable i ("local", because i is actually a copy of the reference that was passed), leaving the variable of the calling method untouched.

Integer is an immutable class, meaning that you cannot change it's value but must obtain a new instance. In this case you don't have to do it manually like this:

i = new Integer(i+1); //actually, you would use Integer.valueOf(i.intValue()+1);

instead, it is done by autoboxing.

Respite answered 25/7, 2010 at 20:35 Comment(0)
C
1

1 ) Only the copy of reference is sent as a value to the formal parameter. When the formal parameter variable is assigned other value ,the formal parameter's reference changes but the actual parameter's reference remain the same incase of this integer object.

public class UnderstandingObjects {

public static void main(String[] args) {

    Integer actualParam = new Integer(10);

    changeValue(actualParam);

    System.out.println("Output " + actualParam); // o/p =10

    IntObj obj = new IntObj();

    obj.setVal(20);

    changeValue(obj);

    System.out.println(obj.a); // o/p =200

}

private static void changeValue(Integer formalParam) {

    formalParam = 100;

    // Only the copy of reference is set to the formal parameter
    // this is something like => Integer formalParam =new Integer(100);
    // Here we are changing the reference of formalParam itself not just the
    // reference value

}

private static void changeValue(IntObj obj) {
    obj.setVal(200);

    /*
     * obj = new IntObj(); obj.setVal(200);
     */
    // Here we are not changing the reference of obj. we are just changing the
    // reference obj's value

    // we are not doing obj = new IntObj() ; obj.setValue(200); which has happend
    // with the Integer

}

}

class IntObj { Integer a;

public void setVal(int a) {
    this.a = a;
}

}

Carse answered 13/8, 2019 at 13:57 Comment(0)
B
0

We can do this using Apache Commons Mutable Int

public static Integer inc(MutableInt i) {
        i.increment();    
        System.out.println("Inc: "+i.getValue());
        return i;
}

public static void main(String[] args) {
        MutableInt integer = new MutableInt(0);
        for (int i =0; i<10; i++){
            inc(integer);
            System.out.println("main: "+integer.getValue());
        }
}

This produces output:

Inc: 1
main: 1
Inc: 2
main: 2
Inc: 3
main: 3
Inc: 4
main: 4
Inc: 5
main: 5
Inc: 6
main: 6
Inc: 7
main: 7
Inc: 8
main: 8
Inc: 9
main: 9
Inc: 10
main: 10
Bushire answered 27/4, 2022 at 13:21 Comment(0)
S
0

A lot of answers have been given to this problem. Most people have suggested wrapper classes.. but we really do not need to create a new class. There is one method which no one has provided.

And problem has been discussed. But I will brief before providing my solution.

Problem arises from the fact that though there is a class Integer which is indeed passed by reference but objects passed by reference are only accessed via methods if you assign a new value that passed reference is lost. Integer class does not provide any methods that allow to modify that value. Using assignment sign on parameter variable means losing the reference that was passed to parameter.

My solution is simpler than using wrapper class. We can just use array of size 1.

    public class PassIntByReference
{
    public static void main(String[] args) throws IOException
    {


        Integer intObj = 0;
        int[] arr = new int[1];

        System.out.println("intObj="+intObj);
        System.out.println("arr[0]="+arr[0]);

        usingIntegerClass(intObj);
        passByRefUsingArray(arr);

        System.out.println("After modifier .. Function call");
        System.out.println("intObj="+intObj);
        System.out.println("arr[0]="+arr[0]);

    }
    public static void usingIntegerClass(Integer immutableRef)
    {
        immutableRef = 1;//This creates a new object and original reference remains unModified.

    }

    public static void passByRefUsingArray(int[] arrOfSize1)
    {
        arrOfSize1[0]=1;
    }
}

output

intObj=0
arr[0]=0
After modifier .. Function call
intObj=0
arr[0]=1
Starveling answered 8/3, 2023 at 10:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.