85

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?

Community
  • 1
  • 1
sixtyfootersdude
  • 25,859
  • 43
  • 145
  • 213
  • Related question: http://stackoverflow.com/questions/4520137/does-java-have-mutable-types-for-integer-float-double-long – koppor Oct 09 '15 at 17:33

11 Answers11

79

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";
}
OwlR
  • 409
  • 1
  • 5
  • 19
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 19
    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. – bwawok Jul 25 '10 at 20:31
  • 34
    Strictly speaking, for references it's "pass reference by value" and for primitives it's just "pass by value". – BalusC Jul 25 '10 at 20:34
  • 2
    well, a duplicate of reference is sent into the function – Rodislav Moldovan Dec 11 '13 at 09:44
  • How is duplicate of reference sent to function different from sending reference to function? Is there any significant difference? – Sreekanth Karumanaghat Aug 29 '16 at 08:46
  • 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). – A.B. May 03 '17 at 05:44
28

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
eebbesen
  • 5,070
  • 8
  • 48
  • 70
Hans
  • 280
  • 3
  • 5
  • What about performance, is it intact? Obviously if `AtomicInteger` is needed only to pass by reference it isn't needed for thread safety. – krizajb Aug 27 '18 at 15:16
  • 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). – PlsWork Feb 27 '20 at 14:14
  • this is real solution, should be at top – Vikrant Pandey Feb 11 '23 at 03:09
26

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);
Markos
  • 1,622
  • 1
  • 16
  • 18
  • 3
    Why Integer is made immutable? – Rahul Jain Jan 20 '16 at 08:56
  • 3
    @RahulJain Not sure, I suspect it is to be consistent with the actual value type (`int`), so they both have the same semantics. – Markos Jan 20 '16 at 11:56
  • 1
    What do you mean by consistent. int can be changed but not Integer, This is kinda downside of the wrapper class. – Rahul Jain Jan 20 '16 at 13:23
  • 2
    @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. – Markos Jan 20 '16 at 13:43
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
Rishi Dua
  • 2,296
  • 2
  • 24
  • 35
16

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 :-)

rsp
  • 23,135
  • 6
  • 55
  • 69
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.

user85421
  • 28,957
  • 10
  • 64
  • 87
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);
        }
    }
bwawok
  • 14,898
  • 7
  • 32
  • 43
  • 1
    huh, ok that is interesting. Thanks for the auto-boxing term. Immutable also seems pretty key. Have a good weekend! – sixtyfootersdude Jul 25 '10 at 20:32
  • 1
    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. – bwawok Jul 25 '10 at 20:35
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.

f1sh
  • 11,489
  • 3
  • 25
  • 51
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;
}

}

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
tryingToLearn
  • 10,691
  • 12
  • 80
  • 114
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
Sandeep Dixit
  • 799
  • 7
  • 12