본문 바로가기
320x100
320x100

이전 글에서는 Reference Type 인자로 전달될때 Call By Value의 동작에 관해서 JNI로 살펴보면서 Integer, AtomicInterger를 통해 객체 타입이 불변/가변으로 나뉠 수 있다는 것을 배웠다

 

이번 글에서는 Reference Type으로 전달 가능한 가변 객체에는 어떤 것들이 있는지 살펴본다

 

Call By Value와 가변 객체를 활용해서 객체를 넘기고 외부의 메서드에서 객체의 상태(값)을 변경하는 예시코드를 더 살펴보자

 

- Array

사용할 배열들
변경할 함수들
main 호출부
결과

- List

List 변경
결과

- Map

요소가 변경되고, 추가된 모습

- Set

Set도 가능
TreeSet은 내부적으로 오름차순으로 정렬한다

 

- Custom Object(Custom Wrapper Class)

이건 내부 구현 로직에 따라 가변or불변으로 구현할 수 있다. 여기서는 가변객체로 만들어본다

 

> 가변 Custom Object

Mutable Wrapper Class
결과

값이 변한 것을 볼 수 있다(Integer로는 위와 같은 경우 값이 변하지 않는다)

 

> 추가질문

1. Array는 맨 처음에 고정된 크기로밖에 만들 수 없는데 어떻게 가변객체로 취급되는걸까?

이 질문을 하는 분은 짐작컨대 아마 헷갈리고(착각하고) 있는 것 같다

[ 왜냐하면 List는 가변배열이니까 Array가 아니지 않나?라고 생각할 수도 있기 때문이다 ]

List도 사실은 Array다(ArrayList == [])

ArrayList의 생성자를 살펴보면 Arrays.copyOf 함수를 사용하고 있다

ArrayList 생성자
Arrays.copyOf

참고로 @Intrinsic-Candidate 어노테이션은 이 코드가 JVM안에서 돌아간다는 뜻이다

그리고 또 이 함수의 내부를 보면 System.arraycopy함수를 사용한다

마치 c언어의 memcpy와 유사하게 동작하는 것을 알 수 있다

다시말해 List도 배열이라고 생각하자

 

다시 질문으로 돌아와서 Java에서 Array나 List가 가변객체로 취급되는 이유는 C 언어에서 배열의 이름이 첫 번째 요소의 포인터 주소를 가리키는 것과 개념적으로 유사한 방식 때문이라고 말할 수 있을 것 같다

Java는 C와 같은 메모리 주소 기반의 포인터를 직접적으로 사용하지 않지만, 배열을 참조형 객체로 다룬다

그리고 Java에서 배열은 객체로 간주되며, 배열의 이름(변수명)은 이 배열 객체를 가리키는 참조(Reference)를 저장한다

결국, 이 참조는 배열이 메모리에 저장된 위치를 가리키기 때문에 배열의 인덱스를 통해 값을 바꿀 수 있는 것이다

 

2. Custom Object를 불변으로 만드는 방법은 없을까?

당연히 있다. Integer와 비슷한 불변 객체를 만들어보자

> 불변 Custom Object

Immutable Wrapper Class
main 호출부
결과

Integer처럼 unboxing/boxing 등의 autoboxing, 연산자 오버로딩을 구현할 수는 없기 때문에 위와같은 방식을 사용했다

 

3. 아하! Collection은 전부 다 가변객체인가요?

과연..?

Java9에서 추가된 of메서드를 통해 인스턴스화된 Collection은 불변이다

정말..?

List.of

of메서드를 통해 생성된 객체에 add를 하려고 하면 UnsupportedOperationException 예외가 발생한다

List.of() 이외에도 Map.of(), Set.of() 메서드를 통해서도 Collection을 만들 수 있다

그리고 당연하게도 다 불변이다

한줄한줄 주석하면서 실행하기 힘들어서 try-catch문으로 놓고 catch로 떨어지는지 테스트해봤다

코드는 이해해줘
실행 결과

예외가 발생해서 다 catch로 떨어지는 걸 확인할 수 있다

 

> 저는 아직 of()메서드에 익숙하지 않고, 코드의 변경보다는 추가를 해서 불변으로 만들고 싶은데.. 방법이 없나요?

아 당근있지있지. 만약에 이렇게 질문한 사람, 신입 개발자 있으면 정말 좋은 개발자다

이미 작성된 코드에서 추가하는 비용보다 수정하는 비용이 더 크다(관심사가 다르다)

수정 - 기존에 그 객체가 사용된 코드를 전부 다 변경해야됨

추가 - 내가 추가한 객체만 작성하면됨

그리고 안전한 테스트코드가 없다면, 리팩토링을 자신있게 할 수 있을까..?

Collections.unmodifiableCollection(...) // 콜렉션

Collections.unmodifiableList(리스트 타입)
Collections.unmodifiableMap(맵 타입)
Collections.unmodifiableSet(셋 타입)

 

Collections의 unmodifiable... 메서드를 사용하면 된다

개인적으로 모든걸 아우르는 Collection보다는 List, Map, Set처럼 명확한 타입을 받는 메서드를 더 선호하는 편이다

가변 Collection을 불변으로 변경
불변이기때문에 지원하지 않음

 

4. 자바를 어느정도 익힌 사람이라면 코틀린을 도전해보자

아래의 코드는 kotest를 작성하는 코드에 그냥 스크린샷으로 올리려고 낑겨넣은 코드다

일단 Java와 비교해보자

Java는 불변객체가 됐는데도 List 인터페이스 자체에서 add() 메서드가 있기때문에 

런타임시에 오류를 발견할 수 있다. 이것을 방지하려면 죄다 try 안에 넣어야 한다

 

하지만 Kotlin은 listOf -> 불변 / mutableListOf -> 가변

그리고 불변에서는 내부의 값을 변경하는 메서드를 사용할 수 없다

이래서 다들 코틀린 코틀린 하는거다.. 물론 Null 처리나 data class 등등 장점도 있지만!!

kotlin

 

마지막으로 Call By Value에 관련해서 Swap을 구현하는 방법에 관해 짧막하게 올리려 한다

320x100

댓글