Java는 객체 지향 패러다임을 지원하지만, 순수 객체 지향 언어가 아니다. Primitive 타입이 있기 때문이다.
객체 지향 패러다임이 지원되기에 객체, 즉 인스턴스가 있고, 인스턴스는 힙 영역에 저장된다. 또한 객체 지향의 시작이 현실 세계에 대한 모방이기에 객체 간의 같음에 대해 이야기 해볼 수 있다.
만약 현실 세계에서 두 음료수 캔이 일련 번호도, 모양도, 생김새도 모두 같다면 그건 같은 음료수 캔이다. 하지만 이걸 Java의 영역에 끌고 들어오면, 서로 다른 생성자로 만들어졌기에 기본적으로는 다른 메모리 공간에 저장된다. 그래서 물리적으론 다르다. 하지만 일련 번호와 같은 속성이 모두 같은 캔이 같은 캔이라고 정의한다면, 이건 논리적으로 같은 캔 객체로 처리해야 할 것이다.
하지만 Java는 캔 따라고 만든 언어가 아니기 때문에,, Primitive의 경우 값이 같으면 같은 것으로 치고, 객체의 경우 참조값을 비교하도록 되어 있다.
이쯤 물리적인 같음과 논리적인 같음을 구분할 필요가 있는데, 쉽게 나누자면 물리적인 같음을 Identity(동일성), 논리적인 같음을 Equality(동등성)이라 한다. 이걸 Java의 세계로 옮겨오자면, primitive type의 값이 같거나, 객체의 참조값이 같으면 이를 Identity라 하고 일련 번호처럼 논리적으로 같은 경우 Equality라 할 수 있겠다. 잘 아시다시피 모든 객체의 부모 클래스는 Object이고, 따라서 참조값으로 equality를 확인하는 기본 로직은 Object에 정의되어 있다.
하지만 우리는 캔의 일련 번호로 논리적인 같음을 구분하고 싶고, 상속이라는 수단을 쥐고 있다. 이렇게!
class Can { // 사실 여기에 extends Object가 있다고 보면 된다!
String id;
String name;
// ...
@Override
public boolean equals(Object foe) {
if (foe == null) {
return false;
}
return (foe instanceof Can) && ((Can) foe).name.equals(this.name);
}
}
근데 Java 1.5에서 추가된 Enum이란 놈은,, 문법부터가 좀 특이하다.
enum Grade {
VIP,
BASIC;
}
그리고 아래 테스트 모두 통과된다.
assertThat(Grade.BASIC).isEqualTo(Grade.BASIC);
assertThat(Grade.BASIC == Grade.BASIC);
어어? Enum이 뭔지 좀 궁금해져서 이것 역시 클래스라면 클래스의 방식 대로 이해할 수 있을 것이기에,, 옆에 이렇게 붙여봤다.
enum Grade extends Object {
VIP,
BASIC;
}
아예 Syntax 오류가 난다. 하지만 정해놓은 적 없던 equality가 알아서 체크가 된다. 왤까?
Enum의 디컴파일된 결과를 보면, 아래와 같이 선언되어 있다.
특이한 게 있다면, 구현(implements)하는 메서드 중 단 하나 toString을 제외하면 모두 오버라이딩이 불가능한 final이거나 아예 상속자에게 은닉된 private으로 되어 있다. 심지어 abstract로 막혀 있어 인스턴스화도 불가능하다.
근데 어떻게 equality check를 하냐면,, JVM을 좀 알아야 한다. (출처)
상수 각각을 내부적으로 public static final 필드이자 객체로 만든다.
static이 붙어 있기 때문에, 각각의 상수는 클래스 변수로 클래스 로더가 로딩 시점에 JVM Method 영역에 해당 클래스를 항상 상주시켜 프로그램이 종료되기 전에는 언제든지 가져다 쓸 수 있는 주소 공간을 확보한다.
이때 실제로 Enum이 사용되는 코드를 만나기 전까지는 주소 공간 확보만 해 놓는데, 사용 코드를 만나면 Heap 영역의, 여기서는 Grade 객체는 VIP와 BASIC까지 각각 java.lang.Enum 클래스를 상속 받는 고유의 객체가 생성되어 1번 과정에서 필드로 만들어 놓은 메서드 영역의 레퍼런스가 힙 영역의 이 객체들을 가리키게 된다.
이러한 전처리 과정이 있기 때문에 identity가 기본적으로 당연히 보장되는 것이고
분석 결과 equality도 위와 같이 identity check로 구현되어 있기 때문에 이렇게 쓰든 저렇게 쓰든 상관이 없는 것이다.