-
[JAVA] equals() - 동일성과 동등성자바 2024. 8. 19. 12:51
자바는 두 객체가 같다는 표현을 2가지로 분리해서 제공한다.
1) 동일성(Identity) : == 연산자를 사용해서 두 객체의 참조가 동일한 객체를 가리키고 있는지 확인
(물리적으로 같은 메모리에 있는 객체 인스턴스인지 참조값을 확인하는 것)
2) 동등성(Equality) : equals() 메서드를 사용하여 두 객체가 논리적으로 동등한 지 확인
package lang.object.equals; public class UserV1 { private String id; public UserV1(String id) { this.id = id; } }
package lang.object.equals; public class EqualsMainV1 { public static void main(String[] args) { UserV1 user1 = new UserV1("id-100"); UserV1 user2 = new UserV1("id-100"); System.out.println("identity = " + (user1 == user2)); // identity = false System.out.println("equality = " + user1.equals(user2)); // equality = false } }
user1과 user2는 논리적으로 같기 때문에 .equals()를 사용했을 때는 true가 나올 것이라고 예상했다.
public boolean equals(Object obj) { return (this == obj); }
Object.equals()를 확인해 보면 결국 return에서 '==' 비교를 하기 때문에 false로 출력될 수밖에 없음을 확인할 수 있다.
따라서 동등성 비교를 사용하고 싶으면 equals() 메서드를 재정의해야 한다.
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; User user = (User) o; return Objects.equals(id, user.id); }
generator 단축키 Alt+Insert 를 사용해 IDE가 만들어 준 equals() 코드를 사용하면 된다.
참고로 동등성 비교가 항상 필요한 것은 아니다. 동등성 비교가 필요한 경우에만 equals()를 재정의하면 된다.
실무에서 .equals()가 참 많이 사용되는 것을 볼 수 있다.
현재 소스가 공개된 프로젝트 코드를 살펴보니 A 프로젝트에서는 재정의 없이 equals()를 사용한 것을 확인했고
B 프로젝트에서는 필요할 때만 Vo에서 재정의해 준 것을 확인할 수 있었다.
String 클래스 비교
String 클래스 비교할 때는 == 비교가 아니라 항상 equals() 비교를 해야한다.
public class StringEqualsMain1 { public static void main(String[] args) { String str1 = new String("hello"); String str2 = new String("hello"); System.out.println("new String() == 비교: " + (str1 == str2)); System.out.println("new String() equals 비교: " + (str1.equals(str2))); // new String() == 비교: false // new String() equals 비교: true String str3 = "hello"; String str4 = "hello"; System.out.println("리터럴 == 비교: " + (str3 == str4)); System.out.println("리터럴 equals 비교: " + (str3.equals(str4))); // 리터럴 == 비교: true // 리터럴 equals 비교: true } }
또한 String에서 객체를 생성했을 때와 리터럴로 생성했을 때의 차이가 있다는 점도 몰랐었다.
String str1 = new String("hello"); String str2 = new String("hello"); System.out.println("new String() == 비교: " + (str1 == str2)); System.out.println("new String() equals 비교: " + (str1.equals(str2))); // new String() == 비교: false // new String() equals 비교: true
String을 객체로 생성했기 때문에 == 비교를 했을 경우 당연히 false, equals비교를 했을 경우 true가 출력된다.
String str3 = "hello"; String str4 = "hello"; System.out.println("리터럴 == 비교: " + (str3 == str4)); System.out.println("리터럴 equals 비교: " + (str3.equals(str4))); // 리터럴 == 비교: true // 리터럴 equals 비교: true
문자열 리터럴을 사용하는 경우 자바는 메모리 효율성과 성능 최적화를 위해 문자열 풀을 사용한다.
자바가 실행되는 시점에 클래스에 문자열 리터럴이 있으면 문자열 풀에 String 인스턴스를 미리 만들어둔다.
이때 같은 문자열이 있으면 만들지 않는다.
String str3 = "hello"와 같이 문자열 리터럴을 사용하면 문자열 풀에서 "hello"라는 문자를 가진 String 인스턴스를 찾는다. 그리고 찾은 인스턴스의 참조를 반환한다. String str4 = "hello"의 경우 "hello" 문자열 리터럴을 사용하므로 문자열 풀에서 str3과 같은 참조를 사용한다.
따라서 ==비교와 equals 비교 모두 true를 반환하게 된다.
이 부분에서 문자열 리터럴을 사용하는 경우 어떻게 동작되는지 모르니 헷갈렸던 부분이 있다. equals()를 사용할 때 오버라이드하라고 했는데 실무에서는 오버라이드한 코드가 거의 없다는 점이었다. 그렇다면 실무에서는 주로 동등성 비교만 한 것인가?라는 의문이 생겼었다. 문자열 리터럴을 사용하는 경우 문자열 풀을 사용하기 때문에 ==비교와 equals() 비교가 동일한 결과를 반환한다는 것을 알게 되고 다시 실무 코드를 보니 대부분이 문자열 리터럴을 사용하는 경우였다.
저번 프로젝트를 진행하면서 생각 없이 == 비교 혹은 equals비교를 사용했다. 그때 당시에 대부분 시니어 개발자분들이 equals()를 사용했기에 따라서 equals를 사용했던 것 같은데... 내가 어디선가에서 ==을 사용했을까 약간 불안하다...
공부를 하고 나니 정말 기본도 모르는 코드를 짰구나 느꼈고 앞으로는 조심해야지.
또한 String을 문자열 리터럴로 생성하는 경우 문자열풀을 사용한다는 점도 기억하자.
(참고)
김영한의 실전자바 - 중급 1편 (섹션1. Object 클래스)
김영한의 실전자바 - 중급 1편 (섹션3. String 클래스)
728x90'자바' 카테고리의 다른 글
[JAVA] Static 키워드 (0) 2024.08.20 [JAVA] 불변 객체 (0) 2024.08.19 [JAVA] Object 클래스 (0) 2024.08.19 [JAVA] 생성자 (0) 2024.08.14 [JAVA] 예외처리 (0) 2023.05.07