java에서 '=='과 equals()는 다르게 쓰인다.
문자열을 비교할 때 equals() 메서드를 사용하는 것은 알겠다.
그런데 이 둘은 내부적으로 무슨 차이를 가지길래 구별해서 쓰고 있는 것일까?
자바는 두 객체가 같다라는 표현을 두 가지로 분리해서 제공한다.
동일성(Identity): == 연산자를 사용해서 두 객체의 참조가 동일한 객체를 가리키고 있는지 확인
동등성(Equality): equals() 메서드를 사용하여 두 객체가 논리적으로 동등한지 확인
'=='연산자와 equals() 메서드의 차이는 바로, '동일'과 '동등'의 차이이다.
'동일'은 완전히 같음을 의미한다. 반면 "동등"은 같은 가치나 수준을 의미하지만 그 형태나 외관 등이 완전히 같지는 않
을 수 있다. 영어로 보면 좀 더 쉽다. 'Identity'와 'Equality'의 차이이다.
쉽게 이야기해서, 동일성은 물리적으로 같은 메모리에 있는 객체 인스턴스인지 참조값을 확인하는 것이고, 동등성은 논리적으로 같은지 확인하는 것이다.
동일성은 자바 머신 기준이고 메모리의 참조가 기준이므로 물리적이다. 반면 동등성은 보통 사람이 생각하는 논리적인
기준에 맞추어 비교한다.
같은 회원 번호를 가진 회원 객체가 2개 있다고 가정해보자.
User a = new User("id-100") //참조 x001
User b = new User("id-100") //참조 x002
이 경우 물리적으로 다른 메모리에 있는 다른 객체이지만, 회원 번호를 기준으로 생각해보면 논리적으로는 같은 회원으
로 볼 수 있다.
따라서 동일성은 다르지만, 동등성은 같다.
문자의 경우도 마찬가지이다.
String s1 = "hello";
String s2 = "hello";
이 경우 물리적으로는 각각의 "hello" 문자열이 다른 메모리에 존재할 수 있지만, 논리적으로는 같은 문자열이다.
이제 실제로 코드를 출력해보자.
Q. 다음과 같이 찍어보면 어떻게 될까?
System.out.println("identity = " + (user1 == user2));
System.out.println("equality = " + user1.equals(user2))
실행 결과는 다음과 같다.
identity = false
equality = false
이상하다. 물리적으로는 다른 메모리이니 동일하지 않다는 것은 알겠다. 그런데 왜 동등하지도 않은가? (아깐 동등하다며!)
🚫Object가 기본으로 제공하는 equals()는 ==으로 동일성 비교를 제공한다.
위와 같은 이유로 인해 false가 나오는 것이다.
ID가 같을 경우, 논리적으로 동등한 결과라고 할 것이라면 equals()메서드를 재정의 해주어야 한다.
Q. 그럼 문자열은 어떨까?
System.out.println("identity = " + (s1 == s2));
System.out.println("equality = " + s1.equals(s2))
실행 결과는 다음과 같다.
identity = true
equality = true
둘 다 true가 나오는 이유는 문자열이 같은 경우 자바가 같은 메모리를 사용하도록 최적화(String Constant Pool ) 하기 때문이다.
그러므로 동일하고, Object가 기본 제공하는 equals()는 동일성을 체크하므로 둘 다 true가 나오는 것이다.
여기서 또 의문이 들 것이다.
아까 문자열은 equals()로 비교한다면서 위의 예제는 왜 true로 나오는가?
다음 예제를 보자.
String s1 = "hello";
String s2 = new String("hello");
System.out.println(s1 == s2); // false: 다른 객체
System.out.println(s1.equals(s2)); // true: 값은 같음
문자열이 리터럴이 아닌 new String()을 통해 생성된 경우, 서로 다른 객체를 참조하므로 '=='는 false를 반환한다.
사용자 입력값이나 데이터 조작으로 생성된 문자열은 동일한 값을 가지더라도 다른 메모리 주소를 참조할 가능성이 높다.
('=='는 리터럴처럼 메모리 최적화가 명확한 경우에만 올바르게 작동)
equals()는 문자열의 값(내용)을 비교하기 때문에 상황에 상관없이 올바른 결과를 보장한다.
위와 같은 이유로 코드의 안정성과 가독성을 위해 문자열 비교에는 항상 equals()를 사용하는 것이 권장된다.
Java SE Documentation - Object.equals()
Java SE Documentation - Java Language Specification (Equality Operators)