일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- github action
- CMC
- 딕셔너리
- Signature
- Payload
- JWT
- 우아한테크코스
- 동등성
- Hackathon
- 라이징캠프
- makeus
- 동일성
- ssh-action
- 동빈북
- CICD
- 우테코
- loop
- 왕실의나이트
- remove
- forloop
- HashCode
- 너디너리
- nestJS
- 이코테
- object
- 나동빈
- equlas
- 컴공선배
- 해커톤
- 이것이취업을위한코딩테스트다
- Today
- Total
iamjooon2님의 블로그
Java final과 불변, 그리고 불변 객체 본문
Java의 final은 크게 변수, 메서드, 클래스에 붙여줄 수 있는 키워드이다
이 final이 붙은 대상은, 재할당이 불가능하다 (불변을 의미하진 않는다)
final은 변수에서 다음과 같이 사용할 수 있다
예시는 자동차 경주 게임이다.
public class Car {
private static final THROTTLE = 4; // 상수에 사용
private final String owner; // 재할당 불가
private int position; // 재할당 가능
public Car(final String name) { // 파라미터에도 붙일 수 있음
this.owner = name;
this.position = 0;
}
public void move(final int power) {
if (power > THROTTLE) {
position++;
}
}
}
상수로 사용한 THROTTLE 부터 보자.
객체 내부에서만 사용할지, 외부에서도 사용할지에 따라 접근 제어자를 설정해두고
static을 사용해 메모리에 미리 적재시킨 다음,
final을 사용하여 재할당을 불가능하도록 하였다.
이처럼 상수를 만들 때 final을 사용할 수 있다
두번째로, final을 붙인 변수인 owner를 보자.
자동차 경주 게임에서 차의 이름은 생성 후 바뀌지 않아, final을 붙여 재할당을 막았다.
만일 final이 붙은 변수에 값을 재할당 하려고 할 시, 컴파일 타임에서 에러가 발생한다.
세번째로 position을 보자.
position은 final 키워드를 붙이지 않았다.
자동차는 메서드로 받은 power의 값에 따라 position이 증가할 수 있어, 재할당 가능하기 때문이다
이처럼 final을 붙이지 않은 변수는 재할당 가능하다.
추가로 메서드 파라미터에도 final을 붙일 수 있다
Argument로 넘어온 변수의 재할당을 막기 위해 사용하였다.
final은 메서드에도 사용할 수 있다
public class Parent {
public final void nonOveridableFunction() {
// do something...
}
}
public class Child extends Parent {
void nonOveridableFunction() {
// 불가
}
}
메서드에 final을 사용하면, 해당 메서드의 오버라이딩을 막을 수 있다
역시 컴파일타임에서 에러를 발생시킨다.
이 final은 클래스에서도 사용가능하다!
public final class Parent {
}
public class Child extends Parent {
// 불가
}
클래스에서 사용한다면, 해당 클래스의 상속을 막을 수 있다.
서두에 final은 재할당 불가의 의미이지, 불변을 뜻하지는 않는다고 적어놨다
아니 지금 쓴거 보면 충분히 불변할 것 같은데 왜?
final List<Integer> numbers = new ArrayList<>();
System.out.println(numbers); // []
numbers.add(1);
System.out.println(number); // [1]
numbers = new ArrayList<>(); // 컴파일 에러
분명 final을 붙였는데 내부 상태의 값이 바뀐 것을 알 수 있다!
그 이뉴는 numbers가 참조하는 ArrayList 객체의 내부 상태를 변경하는 것은 가능하기 때문이다.
numbers.add(1)의 코드에서는 ArrayList 객체의 내부 상태가 변경되어 [1] 이라는 새로운 리스트가 만들어졌다.
final을 붙였지만, 완전한 불변을 보장하지 않는 셈이다!
반대로 numbers = new ArrayList<>(); 에서는 final이 적용된 변수에 새 구현체를 할당하려하니 컴파일 에러가 발생한다.
결국, final은 변수를 재할당하는 것을 방지해 변수의 불변성을 보장할 수 있지만,
해당 변수가 참조하는 객체의 내부 상태는 변경 가능하므로, 완전한 불변을 보장하지는 않는다.
객체의 변수에 final을 붙여준다 해도, 해당 변수가 참조하는 주소값만 불변이고, 그 객체가 또 다시 참조한 주소값은 불변이 아니기 때문이다. (일종의 shallow copy라는 생각이 든다)
그렇다면, 객체에서 불변을 보장하는 방법은 무엇이 있을까?
1.
'공부기록 > Java' 카테고리의 다른 글
Wrapper Class, 그렇게 느린가? (Java Autoboxing 성능 비교) (0) | 2023.06.22 |
---|---|
stream flatMap으로 이중 for loop 없애기 (0) | 2023.05.22 |
방어적 복사가 있으면 공격적 복사도 있나? (0) | 2023.03.06 |
동일성? 동등성? equals? hashcode? (0) | 2023.03.01 |