Java의 기본 요소에 대한 참조에 의한 전달과 동등한 작업을 수행하는 방법


116

이 자바 코드 :

public class XYZ {   
    public static void main(){  
        int toyNumber = 5;   
        XYZ temp = new XYZ();  
        temp.play(toyNumber);  
        System.out.println("Toy number in main " + toyNumber);  
    }

    void play(int toyNumber){  
        System.out.println("Toy number in play " + toyNumber);   
        toyNumber++;  
        System.out.println("Toy number in play after increement " + toyNumber);   
    }   
}  

다음과 같이 출력됩니다.

 
놀이 5 장난감 번호  
기소 6 이후 놀이 장난감 번호  
메인 5의 장난감 번호  

C ++에서는 toyNumber섀도 잉을 방지하기 위해 참조에 의한 전달로 변수를 전달할 수 있습니다. 즉, 아래와 같은 동일한 변수의 복사본을 생성합니다.

void main(){  
    int toyNumber = 5;  
    play(toyNumber);  
    cout << "Toy number in main " << toyNumber << endl;  
}

void play(int &toyNumber){  
    cout << "Toy number in play " << toyNumber << endl;   
    toyNumber++;  
    cout << "Toy number in play after increement " << toyNumber << endl;   
} 

C ++ 출력은 다음과 같습니다.

놀이 5 장난감 번호  
기소 6 이후 놀이 장난감 번호  
메인 6의 장난감 번호  

내 질문은 -Java가 참조로 전달하는 것이 아니라 값으로 전달되는 경우 C ++ 코드와 동일한 출력을 얻기 위해 Java에서 동등한 코드는 무엇입니까 ?


22
이것은 나에게 중복 된 것처럼 보이지 않습니다. 중복 된 질문은 자바가 작동하는 방식과 용어의 의미, 즉 교육을 다루는 반면,이 질문은 참조에 의한 통과 동작과 유사한 것을 얻는 방법, 많은 C를 얻는 방법을 구체적으로 묻습니다. / C ++ / D / Ada 프로그래머는 실제 작업을 수행하기 위해 궁금해 할 수 있습니다. Java가 모두 값으로 전달되는 이유는 신경 쓰지 않습니다.
DarenW

1
@DarenW 전적으로 동의합니다-재개하기로 투표했습니다. 오, 그리고 당신은 똑같이 할 수있는 충분한 명성을 가지고 있습니다 :-)
Duncan Jones

질문이 참조 값에 동일하게 적용되기 때문에 기본 요소에 대한 논의는 다소 오해의 소지가 있습니다.
shmosel

"C ++에서는 toyNumber 변수를 참조로 전달하여 섀도 잉을 방지 할 수 있습니다." - 메서드에 toyNumber선언 된 변수가 main메서드의 범위에 있지 않기 때문에 이것은 섀도 잉 이 아닙니다 play. C ++ 및 Java의 섀도 잉은 범위가 중첩 된 경우에만 발생합니다. en.wikipedia.org/wiki/Variable_shadowing을 참조하십시오 .
Stephen C

답변:


171

몇 가지 선택이 있습니다. 가장 의미있는 것은 당신이하려는 일에 달려 있습니다.

선택 1 : toyNumber를 클래스의 공용 멤버 변수로 만들기

class MyToy {
  public int toyNumber;
}

그런 다음 MyToy에 대한 참조를 메서드에 전달하십시오.

void play(MyToy toy){  
    System.out.println("Toy number in play " + toy.toyNumber);   
    toy.toyNumber++;  
    System.out.println("Toy number in play after increement " + toy.toyNumber);   
}

선택 2 : 참조로 전달하는 대신 값 반환

int play(int toyNumber){  
    System.out.println("Toy number in play " + toyNumber);   
    toyNumber++;  
    System.out.println("Toy number in play after increement " + toyNumber);   
    return toyNumber
}

이 선택은 main의 callsite를 약간 변경하여 toyNumber = temp.play(toyNumber);.

선택 3 : 클래스 또는 정적 변수로 만들기

두 함수가 동일한 클래스 또는 클래스 인스턴스의 메서드 인 경우 toyNumber를 클래스 멤버 변수로 변환 할 수 있습니다.

선택 4 : int 유형의 단일 요소 배열을 만들고 전달합니다.

이것은 해킹으로 간주되지만 때때로 인라인 클래스 호출에서 값을 반환하는 데 사용됩니다.

void play(int [] toyNumber){  
    System.out.println("Toy number in play " + toyNumber[0]);   
    toyNumber[0]++;  
    System.out.println("Toy number in play after increement " + toyNumber[0]);   
}

2
명확한 설명과 특히 원하는 효과를 위해 코딩하는 방법에 대한 다중 선택을 제공하는 데 유용합니다. 지금 작업중인 작업에 직접적으로 도움이됩니다! 이 질문이 종결 된 것은 미쳤습니다.
DarenW

1
당신의 선택 1은 당신이 전달하려는 것을 수행하지만 실제로는 돌아가서 다시 확인해야했습니다. 이 질문은 참조로 통과 할 수 있는지 묻습니다. 그래서 정말로 당신은 함수에 전달 된 장난감이 존재하는 것과 같은 범위에서 printlns를 수행 했어야했습니다. 당신이 그것을 가지고있는 방식으로, printlns는 COURSE의 요점을 실제로 증명하지 않는 증분과 동일한 범위에서 작동합니다. 증분 후에 인쇄 된 로컬 사본은 다른 값을 갖지만 전달 된 참조가 있다는 의미는 아닙니다. 다시 말하지만, 귀하의 예제는 작동하지만 요점을 증명하지는 않습니다.
zero298 2013-08-27

아니, 나는 그것이 올바른 것 같아요. 위의 답변에서 각 함수 "play ()"는 원래 "play ()"함수의 드롭 인 대체로 의도되었으며 증가 전후의 값도 인쇄했습니다. 원래 질문에는 참조로 통과했음을 증명하는 println ()이 main에 있습니다.
laslowh

선택 2에는 추가 설명이 필요합니다 toyNumber = temp.play(toyNumber);. 원하는대로 작동 하려면 main의 호출 사이트를로 변경해야합니다 .
ToolmakerSteve

1
이것은 매우 추하고 끔찍한 느낌을 가지고 있습니다. 왜 그렇게 기본적인 것이 언어의 일부가 아닌가?
shinzou

30

자바가 아닌 참조에 의한 호출 이있다 값에 의해 호출은

그러나 객체 유형의 모든 변수는 실제로 포인터입니다.

따라서 Mutable Object를 사용하면 원하는 동작을 볼 수 있습니다.

public class XYZ {

    public static void main(String[] arg) {
        StringBuilder toyNumber = new StringBuilder("5");
        play(toyNumber);
        System.out.println("Toy number in main " + toyNumber);
    }

    private static void play(StringBuilder toyNumber) {
        System.out.println("Toy number in play " + toyNumber);
        toyNumber.append(" + 1");
        System.out.println("Toy number in play after increement " + toyNumber);
    }
}

이 코드의 출력 :

run:
Toy number in play 5
Toy number in play after increement 5 + 1
Toy number in main 5 + 1
BUILD SUCCESSFUL (total time: 0 seconds)

표준 라이브러리에서도이 동작을 볼 수 있습니다. 예를 들어 Collections.sort (); Collections.shuffle (); 이 메서드는 새 목록을 반환하지 않지만 인수 개체를 수정합니다.

    List<Integer> mutableList = new ArrayList<Integer>();

    mutableList.add(1);
    mutableList.add(2);
    mutableList.add(3);
    mutableList.add(4);
    mutableList.add(5);

    System.out.println(mutableList);

    Collections.shuffle(mutableList);

    System.out.println(mutableList);

    Collections.sort(mutableList);

    System.out.println(mutableList);

이 코드의 출력 :

run:
[1, 2, 3, 4, 5]
[3, 4, 1, 5, 2]
[1, 2, 3, 4, 5]
BUILD SUCCESSFUL (total time: 0 seconds)

2
이것은 질문에 대한 답이 아닙니다. 단일 요소를 포함하는 정수 배열을 만든 다음 메서드 내에서 해당 요소를 수정하도록 제안했다면 질문에 답했을 것입니다 play. 예 : mutableList[0] = mutableList[0] + 1;. Ernest Friedman-Hill이 제안한대로.
ToolmakerSteve

원시가 아닌 것. 개체의 값은 참조가 아닙니다. "매개 변수 값"은 "참조"입니다. 참조는 값으로 전달됩니다.
vlakov

비슷한 것을 시도하고 있지만 코드에서 메서드 private static void play(StringBuilder toyNumber)-예를 들어 정수를 반환하는 public static int 인 경우 어떻게 호출합니까? 숫자를 반환하는 메서드가 있지만 어딘가에 전화하지 않으면 사용되지 않습니다.
frank17 2015

18

을 만들다

class PassMeByRef { public int theValue; }

그런 다음 인스턴스에 대한 참조를 전달하십시오. 인수를 통해 상태를 변경하는 메서드는 특히 병렬 코드에서 피하는 것이 가장 좋습니다.


3
내가 비추천했습니다. 이것은 많은 수준에서 잘못된 것입니다. Java는 항상 값으로 전달됩니다. 예외 없음.
duffymo 2011

14
@duffymo-물론 당신은 당신이 원하는대로 다운 투표 할 수 있습니다-그러나 OP가 요청한 것을 고려 했습니까? 그는 참조로 전달하고 int를 원합니다. 위의 인스턴스에 대한 참조를 값으로 전달하면 이것이 수행됩니다.
Ingo 2011-04-11

7
@ duffymo : 응? 당신은 착각하거나 질문을 오해합니다. 이것은 IS 무엇 OP 요청을하는 자바의 전통적인 방법 중 하나.
ToolmakerSteve

5
@duffymo : 당신의 댓글은 너무 많은 레벨에서 잘못되었습니다. SO는 유용한 답변과 코멘트를 제공하는 것이며, 프로그래밍 스타일과 철학에 맞지 않기 때문에 정답을 단순히 반대표를 던집니다. 최소한 원래 질문에 대해 더 나은 설명이나 더 나은 답변을 제공 할 수 있습니까?
paercebal

2
@duffymo는 정확히 내가 말한 것입니다 : 값으로 ref를 전달하십시오. 참조에 의한 전달이 속도를 늦추는 언어로 어떻게 이루어 졌다고 생각하십니까?
Ingo

11

Java에서 참조로 기본 요소를 전달할 수 없습니다. 물론 객체 유형의 모든 변수는 실제로 포인터이지만 "참조"라고 부르며 항상 값으로 전달됩니다.

참조로 기본 요소를 전달해야하는 상황에서 사람들은 매개 변수를 기본 유형의 배열로 선언 한 다음 단일 요소 배열을 인수로 전달하는 경우가 있습니다. 따라서 참조 int [1]을 전달하고 메서드에서 배열의 내용을 변경할 수 있습니다.


1
아니요-모든 래퍼 클래스는 변경할 수 없습니다. 개체가 생성되면 변경할 수없는 고정 값을 나타냅니다.
Ernest Friedman-Hill

1
"당신은 자바에서 참조로 프리미티브를 전달할 수 없다": OP는 자바와 C ++ (당신이 할 수있는 곳)를 대조하기 때문에 이것을 이해하는 것 같다.
Raedwald 2014

Ernest가 말한 "모든 래퍼 클래스는 변경할 수 없습니다"는 JDK의 내장 Integer, Double, Long 등에 적용된다는 점에 유의해야합니다. Ingo 's Answer가 한 것처럼 사용자 지정 래퍼 클래스로이 제한을 우회 할 수 있습니다.
user3207158

10

빠른 솔루션을 위해 AtomicInteger 또는 내장 된 메서드를 사용하여 메서드 내부의 값을 변경할 수있는 원자 변수를 사용할 수 있습니다. 다음은 샘플 코드입니다.

import java.util.concurrent.atomic.AtomicInteger;


public class PrimitivePassByReferenceSample {

    /**
     * @param args
     */
    public static void main(String[] args) {

        AtomicInteger myNumber = new AtomicInteger(0);
        System.out.println("MyNumber before method Call:" + myNumber.get());
        PrimitivePassByReferenceSample temp = new PrimitivePassByReferenceSample() ;
        temp.changeMyNumber(myNumber);
        System.out.println("MyNumber After method Call:" + myNumber.get());


    }

     void changeMyNumber(AtomicInteger myNumber) {
        myNumber.getAndSet(100);

    }

}

산출:

MyNumber before method Call:0

MyNumber After method Call:100

이 솔루션은 좋은 복합 동작을 제공하고 이미 또는 미래에 이러한 원자 클래스를 사용할 수있는 동시 프로그램에 통합하기가 더 쉽기 때문에이 솔루션을 사용하고 좋아합니다.
RAnders00

2
public static void main(String[] args) {
    int[] toyNumber = new int[] {5};
    NewClass temp = new NewClass();
    temp.play(toyNumber);
    System.out.println("Toy number in main " + toyNumber[0]);
}

void play(int[] toyNumber){
    System.out.println("Toy number in play " + toyNumber[0]);
    toyNumber[0]++;
    System.out.println("Toy number in play after increement " + toyNumber[0]);
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.