IT TIP

Integer 클래스를 참조로 올바르게 전달할 수 있습니까?

itqueen 2021. 1. 10. 19:45
반응형

Integer 클래스를 참조로 올바르게 전달할 수 있습니까?


나는 누군가가 나를 위해 여기서 무슨 일이 일어나고 있는지 명확히 할 수 있기를 바랍니다. 나는 비트 정수 클래스에 주위 파고하지만 정수 때문에 무시+ 연산자를 내가 잘못을 무슨 일이 있었는지 알아낼 수 없었다. 내 문제는 다음 줄에 있습니다.

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

내 추론은 다음과 같습니다. java가 값으로 전달됨 ( 또는 참조 값으로 전달됨 )을 알고 있으므로 다음 예제에서 정수 객체가 매번 증가되어야한다고 생각합니다.

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);
        }
    }
}

이것은 내 예상 출력입니다.

Inc : 1
메인 : 1
Inc : 2
메인 : 2
Inc : 3
메인 : 3
Inc : 4
메인 : 4
Inc : 5
메인 : 5
Inc : 6
메인 : 6
...

이것은 실제 출력입니다.

Inc : 1
메인 : 0
Inc : 1
메인 : 0
Inc : 1
메인 : 0
...

왜 이렇게 작동합니까?


두 가지 문제가 있습니다.

  1. 정수는 참조가 아닌 값으로 전달됩니다. 메서드 내에서 참조를 변경하면 호출 메서드에서 전달 된 참조에 반영되지 않습니다.
  2. 정수는 변경할 수 없습니다. 같은 방법은 없습니다 Integer#set(i). 그렇지 않으면 그냥 사용할 수 있습니다.

작동하도록하려면 inc()메서드 의 반환 값을 다시 할당해야합니다 .

integer = inc(integer);

값으로 전달하는 방법에 대해 자세히 알아 보려면 다른 예가 있습니다.

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";
}

Integer는 불변입니다. 사용자 정의 래퍼 클래스에서 int를 래핑 할 수 있습니다.

class WrapInt{
    int value;
}

WrapInt theInt = new WrapInt();

inc(theInt);
System.out.println("main: "+theInt.value);

OP의 실제 질문을 설명하는 위의 좋은 답변입니다.

누군가 전역 적으로 업데이트해야하는 번호를 전달해야하는 경우 AtomicInteger(제안 된 다양한 래퍼 클래스를 만들거나 타사 라이브러리에 의존하는 대신)를 사용하십시오 .

AtomicInteger()은 대부분 스레드로부터 안전 액세스에 사용 물론이지만 성능 저하가 전혀 문제가없는 경우, 이유는 내장 클래스 사용하지 마십시오. 추가 된 보너스는 물론 명백한 스레드 안전성입니다.

import java.util.concurrent.atomic.AtomicInteger

참조로 전달하는 방법에는 두 가지가 있습니다.

  1. Apache Commons 라이브러리에서 org.apache.commons.lang.mutable.MutableInt사용하십시오 .
  2. 아래와 같이 사용자 정의 클래스를 만듭니다.

이를 수행하는 샘플 코드는 다음과 같습니다.

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;
    }
}

산출:

1
1
7
7

여기에서보고있는 것은 과부하 된 +oparator가 아니라 오토 박싱 동작입니다. Integer클래스는 불변 코드입니다 :

Integer i = 0;
i = i + 1;  

컴파일러에서 (오토 박싱 후) 다음과 같이 표시됩니다.

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

따라서 Integer인스턴스가 변경 되었다는 결론은 정확 하지만 은밀하게는 아닙니다-Java 언어 정의와 일치합니다 :-)


여기에서 맞습니다 :

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

첫째 : 정수는 불변입니다.

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.


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);
        }
    }

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.


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;
}

}

ReferenceURL : https://stackoverflow.com/questions/3330864/how-can-i-pass-an-integer-class-correctly-by-reference

반응형