Java, equals - 동일성 그리고 동등성
Object 클래스는 동등성 비교를 하는 equals()
메소드를 제공.
자바 언어에서의 두 객체가 같다의 표현은 2가지로 분리되어있다.
두 객체가 같다
- 동일성(identity)
- 동등성(Equality)
동일성은 "==" 연산자를 사용하여 두 객체의 참조가 동일한 객체를 가리키는지 확인한다.
동등성은 "equals()" 메소드를 사용한 두 객체가 논리적으로 동등한지 확인한다.
단어 정리
동일의 의미는 두 개 모두 완전히 같다로 의미한다.
동등의 의미는 가치와 수준이 같지만, 그 형태와 외관이 다를 수있다.
사용 예시
동일성은 메모리에 있는 객체 인스턴스의 참조 값을 확인한다.
동등성은 논리적으로 같은지 확인한다.
메모리의 참조는 물리적이며, 동등성은 사람이 생각하는 논리적인 기준을 제시하고 비교한다.
Class a = Class("A Class") // x0000001
Class b = Class("A Class") // x0000002
a 와 b의 객체는 메모리에서는 다르지만, 클래스 기준으로 봤을 때는 같은 반이라는 것을 인식한다.
이는 동일성은 다르지만, 동등성은 같다.
문자로 사용하였을 때도 같은 의미로 사용한다.
String str1 = "world";
String str2 = "world";
서로 다른 메모리로 가리키지만 같은 "world" 문자열을 담고 있다.
비교하기
이제 "==", "equals()" 사용한 비교를 해보도록 한다.
system.out.println("identity=" + (a == b));
system.out.println("equality=" + (a.equals(b)));
결과는 동일한 false 값을 출력한다. 자바 Object 에서의 equals 기능은 다음과 같이 정의되어 있다.
Object.equals()
public boolean equals(Object obj) {
return (this == obj);
}
기본적으로 자바는 동일성만 비교할 수 있지만, 동등성을 비교하려면 사용자가 메소드 재정의하여 사용해야 한다.
equals() 구현하기
새로운 클래스를 정의한다.
ClassV2.java
public class ClassV2 {
private String name;
public ClassV2(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
ClassV2 class = (ClassV2) obj;
return name.equals(class.name);
}
}
- 타입의 매개변수로 Object 타입으로 사용하였다. equals() 비교에는 명시적인 다운캐스팅이 필요하다.
- this.name 자신의 변수는 Object 넘어온 name 문자열과 비교하게 된다.
- 문자열 비교는 "==" 사용하지 않고 "equals()" 메소드를 이용해야한다.
ClassV2Main.java
public Class ClassV2Main {
public static void main(Args[] args) {
ClassV2 a = ClassV2("A Class");
ClassV2 b = ClassV2("A Class");
system.out.println("identity=" + (a == b));
system.out.println("identity=" + (a.equals(b)));
}
}
출력 결과
identity=false
equality=true
equals 메소드가 동등성을 비교할 수 있도록 재정의되어 true 문자열을 반환하는 것을 볼 수 있다.
정확한 equals() 구현 규칙
실제로 equals 동등성을 정확하게 동작하려면 몇 가지 규칙이 지켜야 한다.
IntelliJ 와 같은 IDE 에서는 동등성을 쉽게 만들 수 있도록 equals() 코드를 자동으로 생성한다.
IntelliJ 윈도우 유저인 경우 재정의한 equals() 메소드를 삭제한 다음 equals() Alt + Insert 눌려 equals() 메소드를 자동 생성한다.
IDE 자동 생성 equals() 메소드
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null | getClass() != p.getClass()) return false;
ClassV2 classV2 = (ClassV2) o;
return Objects.equals(name, classV2.name);
}
equals() 메소드 구현 시 지켜야 규칙
equals 구현한 경우 안전하게 사용하기 위해서는 규칙을 지켜야 한다.
- 반사성(Reflexivity): 객체는 동등해야 한다. (x.equals(x) == true)
- 대칭성(Symmetry): 두 객체가 양방향으로 동일해야한다. (x.equals(x) == y.equals(y))
- 추이성(Transitivity): x 객체가 y 와 동일하고 y와 z는 서로 동일하다면 x 와 z 는 동일하다.
- 일관성(Consistency): 객체가 변경되지 않는 한, 값은 변경되지 않아야한다.
- null 비교: 모든 객체는 null 값과 비교한 경우 false 값이어야 한다.
정리하기
- equals() 메소드로 동일성을 비교하고 싶은 경우 메소드 재정의한다.
- equals() 와 함께 hashcode() 메소드도 자동 생성된다.