Frudy
7k
2019-05-27 22:53:44 작성 2019-05-27 22:58:37 수정됨
8
4208

Java가 Call by value인 이유


이 사이트에서 도움받은게 되게많아서,

제가 갖고있는 지식 하나 공유하고자 해요.


제가 아직 학생이라... 혹여나 잘못된 정보를 알려드리는게 아닐까 싶어서,

제일 정확한 정보로 가져왔슴니다.


잘못된 부분이 있다면 지적 부탁드립니다.



package myutil.test;

class Test 
{
	public static void main(String[] args) 
	{
		Person seohyun = new Person("서현");
		change(seohyun);
		print(seohyun);
	}
	
	public static void change(Person person)
	{
		person = new Person("김태희");
	}
	
	public static void print(Person person)
	{
		System.out.println(person.getName());
	}
}

class Person
{
	String name;
	
	Person(String name)
	{
		this.name = name;
	}
	
	void setName(String name)
	{
		this.name = name;
	}
	
	String getName()
	{
		return name;
	}
}


과연, 이름은 "서현"에서 "김태희"로 바뀔까요 안바뀔까요?

안바뀌는 이유를 아시는 분은 이 글을 안읽으셔도 좋습니다.


정답은, 바뀌지 않습니다.



처음부터 설명을 적어볼게요.


1. 변수에 저장된 값을 바꾸는 방법은?


int age = 4; 하는 순간 메모리에서 어떤일이 생기는지부터 알아볼게요.

1. 메모리에는 데이터를 저장할 수 있습니다.

2. 그 메모리를, 구역별로 나눴는대, 그중 하나가 Stack이라는 영역입니다.


책가방도, 영역별로 나뉘어져있습니다.

책놓는곳, (젤큰곳)

칫솔도구 놓는곳(젤 앞 쪼꼬만한 곳)


int age = 4; 라고 하는순간, 메모리에서는

Stack이라는 영역에, 4라는 값이 저장됩니다.


(정확히는, 4는 십진수값이기 때문에, 사실 2진수로 저장됩니다)


하지만...?

어떤 값을 저장하기 편한 방법중 하나가. 주소를 이용하는 것입니다.


아파트 1층 우편함에 그 아파트 주민들의 우편물을 저장하는 가장편한방법은,

우편함마다 호수를 적는것이죠.


그래서, 4라는 값을 저장되어있는 위치가 어디냐! 라고 물어본다면

100000번지에 저장되어있습니다 라고 답변하면 되겠습니다.



그럼, age라는 변수에 저장된값을 바꾸려면 어떻게 해야할까요?

age라는 변수가 저장된 위치에 접근해야합니다.

지극히 당연한 이치입니다.


그 다음으로 알아볼것은, 

primitive type과

reference type입니다.



String name = "서현"; 이라고 할 경우,

과연 위 사진처럼 저장이 될까요?


아닙니다. 이렇게 저장이 됩니다.

메모리 구역중에 Stack말고, Heap이라는 곳도 있습니다.

실제 데이터는 Heap에 저장되고, Stack에는 그 시작주소만 저장됩니다.


이것이, primitive타입과, reference타입의 차이입니다.


primitive type)

int같은 타입의 변수에는 주소형태의 값이 저장되지않습니다.

age에는 4가 저장됬었죠. 4는 번지수인가요? 아니에요 값이에요.


reference type)

String같은 타입의 변수에는 주소형태의 값이 저장됩니다.

name에 "서현"이 저장되나요? 아니에요. "서현"이라는 값이 저장된 위치의 주소값을 저장할뿐이에요.


reference타입이란, primitive타입이 아닌 모든 타입을 뜻합니다.

즉 우리가 직접 클래스를 선언해서 객체를 생성한다면,

그것은 반드시 reference타입입니다.


예로 아까 만든 Person은 reference type입니다.


그렇다면, 다시 질문을 해보겠습니다.

"서현" 대신에 "김태희"로 수정하려면,

1번으로 접근해야할까요 2번으로 접근해야할까요?

정답은, 둘 다 가능하다 입니다.


방법은 2가지입니다.

main()에서

Person seohyun = new Person("김태희"); 하는 경우,


기존의 서현객체 대신, 아예 새로운 김태희객체를 저장하게 되는 경우 이구요,



change()에서

seohyun.setName("김태희");할 수도 있습니다.

직접 서현객체로 접근해서, 객체에 저장된 "서현" 속성을 "김태희"속성으로 바꾸는 것입니다.


쉽게 비유하면... 음....

제가 100평짜리 아파트에 사는 방법은,

1. 그냥 100평짜리 아파트로 이사가기(주소이전)

2. 제가 사는 아파트를 100평으로 확장하기(주소동일)


요런 느낌입니다.


저 행위가 가능한 이유는, "서현"객체의 주소값이 Stack에 저장되어있기 때문이에요.

제가 사는 아파트를 100평으로 확장하려면,

제가 사는 아파트 주소를 알아야 가서 바꿀수있으니까요.



이제 call by reference가 아닌 call by value인 이유를 알기위한

모든 기초가 설명이 되었어요.


그럼이제, call by reference랑 call by value가 뭔지 알려드리고,

왜 call by value인지를 설명해볼게요.


call by xxxx는, 함수호출 방식이에요.


아까, change(seohyun); 이렇게 호출했었죠?


main()에서 change(seohyun); 이렇게 매개값을 전달할 때

내부적으로 과연 어떻게 전달되는지 궁금하지않으세요?



이렇게 작동됩니다.


1. main()의 Person변수와, change()의 Person변수는 엄연히 다릅니다. 정말 달라요.

2. 그대신, 참조하고있는 (= 저장된) 객체의 주소는 동일합니다.


이것이 call by value입니다.

Person변수에 저장된 주소값을 복사해서 메소드에게 주는거에요.


그렇다면 이야기는 이제 간단해요.

change(Person person)

{

    person = new Person("김태희"); 이 행동이 어떤행동일까요?

}

바로 이런 행동입니다.

우리가 김태희객체를 대입한 변수는,


1. main()에 있는 Person변수인가요 아니면...

2. change()에 있는 Person변수인가요?


change()에 있는 Person변수에 김태희객체의 주소값을 저장했는데,

main()에 있는 Person변수에 김태희객체의 주소값이 저장될리가 없죠.


 이것이 어떤느낌이냐면요,


int num = 3;

int number; 이렇게 두개의 변수가 선언되있는대,

num변수의 값을 바꾸겠다고 number변수에 값을 대입한거에요.


num변수, number변수 이 둘은 완전히 다른 변수인대 말이죠.


그래서, 매개로 넘겨주면서, 수정도 할 수 있는 방법은?

person = new Person("김태희"); 가 아니라,

person.setName("김태희");가 되어야 합니다.


잘못된 부분의 지적은 감사히 받겠습니다.

읽어주셔서 감사합니다.

10
7
  • 댓글 8

  • Hyperglide
    393
    2019-05-27 23:14:31
  • zepinos
    20k
    2019-05-27 23:30:14
    내용 자체는 오류는 없어 보이네요.
    다만, 사견을 적자면 Java 가 정확한 call by ref. 가 아니라서 call by value 다...라고 하면 혼동의 여지가 있다고 저는 생각하는 편이고, Java 개발할 때 매개변수를 ref. 하지 않는 형태로 개발하는 기존의 코딩 관습이 매우 명확하기 때문에 위 예제와 같은 코딩은 지양해야 한다고 봅니다. return 으로 매개변수 중 재사용할 것들을 받이서 사용하는게 명확하다는거죠. 제 오만이나 편견일 수 있지만, 저런 지식은 실제 개빌 시 거의 생각할 필요가 없도록 작성되는게 더 옳은, 실험실 코드(?)라고 명명하고픈 코드입니다.

    물론, 아직 실무에 본격적으로 나서기 전의 주니어들은 한 번 쯤은 다시 볼 필요가 있는 좋은 정보글이라고 생각합니다.

  • 아야로
    1k
    2019-05-28 03:36:37

    좋네요~

    자바는 이렇게 명확한데, 자바스크립트는 스코프 관련 어쩜 그리 사람 헷갈리게 만드는지...

    저도 프루디 님처럼 깔끔하게 정리할 날이 오면 좋겠네요 ㅎㅎ

  • 배우고싶은이
    734
    2019-05-28 10:14:18

    아마도 ref. 하지 않는 형태로 개발.... 이란 뜻은..


    person의 이름을 바꾸고 싶을때..


    void change(Person person){

      person.setName("태희");

    }

    이런식으로 하지 않는다는 겁니다.


    최초 Person을 New한 곳에서(변수 스콥이 있는 블럭 - 클래스 또는 함수) setXXX을 호출 하는것이 맞다는 얘기 같습니다.


    change에서 새로운 Person객체를 만든다면 명확하게 return type을 주고 호출하는 쪽에서 할당을 해줘야 하는거죠.


    person = change(person);


    .

    .

    .


    Person change(Person aaa){

      return new Person("태희");

    }


    뭐 대강 이런내용이 아닌럴지요.


    듣기로 C에서는 리턴값이 없는 것을 프로시져, 리턴값이 있는 것을 함수라고 별도로 부른다고 하더군요.

    프로시져의 경우 레퍼런스로 받고 내부 값을 조작한다고 알고 있습니다.(포인터형)


    뭐 어째든 한번은 짚고 넘어갈 부분을 잘 정리하신 듯 합니다.

    저도 자바만 합니다만 자바하시는 분들이 메모리에서 일어나는 일에 무관심한 경우가 많더라구요.

  • 배우고싶은이
    734
    2019-05-28 10:27:23

    Frudy

    제가 아는 것이 정답이 아닐수도 있습니다. 너무 상심하지 마시길..


    단지 특정 레퍼런스의 값이 블럭 안에서 여러 함수의 호출 파라메터로 사용되는 과정에 그 값이 달라지면 유지보수 및 가독성 등등이 매우 떨어지게 됩니다.

    또하나 문제는 모듈간 강결합이 될 가능성이 매우 높아지게 됩니다.


  • charlatan
    3k
    2019-05-28 11:31:46

    그런데 함수 파라미터 뿐만아니라 레퍼런스 타입이라도 String처럼 immutable 클래스가 았고 List 같은 컬렉션들은 일반적인 객체처럼 동작하니 혼란스러운 부분이 있는 것 같습니다.

    아무튼 무엇을 하는지 알고 사용하면 문제 없는데 잘못 사용하면 원본이 바뀌거나 또는 반대로 변경되는 줄 알았는데 안된다거나 하는 상황이 생기는 것이죠.

  • norman
    33
    2019-05-28 11:44:33

    로버트 C 마틴 클린코드나 클린코더스 보면 

    CQS(Command Query Separation)라고 하죠.

    상태를 변경하는 함수는 값을 반환하면 안된다. 값을 반환하는 함수는 상태를 변경하면 안된다.

    cf) https://yangbongsoo.gitbook.io/study/clean_coders

  • 스타
    3k
    2019-05-31 17:04:23 작성 2019-05-31 17:05:33 수정됨

    기본 정의를 잘 못 이해한 것 아닌가 하는 생각이 드는데요.. 제 의견은 이렇습니다.

    Call by reference를 지원한다는 의미를 잘 못 생각하신 것 같습니다. 이게 아니면 저거.. 이분법적인 사고죠.

    Call by reference의 기반이 되는 객체의 주소를 바꾸는 예제는 개발시 주의사항에 해당 할 것 같기 때문입니다.

    만일 넘겨진 객체의 속성을 변경하여 증명한다면 저도 동의할 것 같습니다.

  • 로그인을 하시면 댓글을 등록할 수 있습니다.