[CS] Kotlin과 Java의 동일성, 동등성
이번에는 객체 공부를 할 때 쉽게 헷갈릴 수 있는 동일성과 동등성에 대해 공부해 보았다. 또한 Kotlin과 Java에서는 어떻게 다르게 쓰이는 지도 정리했다.
우선, 이름부터 헷갈리기 쉬운 동일성과 동등성의 개념에 대해 알아보자.
동일성
Identity, 영어로 하면 이해기 쉽다. 객체의 메모리 내 주소값이 같은지 식별한다.
즉, 내용이 같더라도 주소가 다르다면 다른 객체로 판단하는 것이다.
동등성
Equality, 논리적으로 같은 지위를 지녔는지 확인하는 것이다.
즉, 변수가 참조하고 있는 객체의 주소가 서로 다르더라도 내용만 같으면 두 변수는 동등하다고 본다.
Java의 동일성, 동등성
그럼 Java에서는 동일성과 동등성이 어떻게 판단되는지 알아보자.
String str1 = new String("Bona");
String str2 = new String("Bona");
System.out.println(str1 == str2); // false
System.out.println(str1.equals(str2)); // true
- 동일성은 == 연산을 통해 확인한다.
- 위 코드에서 new를 통해 다른 String 객체를 메모리에 할당하였으므로 str1과 str2의 주솟값은 달라서 false
- 동등성은 equals 연산을 통해 확인한다.
- 위 코드에서 두 객체의 내용이 같기 때문에 true
여기서 주의해야 할 점은 equals 연산자로 항상 동등성을 판단할 수 있는 것은 아니다.
public class Main {
public static void main(String[] args) {
Student student1 = new Student("Bona");
Student student2 = new Student("Bona");
System.out.println(student1 == student2); // false
System.out.println(student1.equals(student2)); // false
}
public static class Student {
private final String name;
public Student(String name) {
this.name = name;
}
}
}
Object 객체로 비교를 하게 된다면 == 연산과 equals 연산 모두 false가 뜨는 것을 확인할 수 있다.
왜 이런 결과가 나오게 되는 것일까?🤔
그 이유는 eqauls가 아래처럼 정의되어 있기 때문이다.
public boolean equals(Object obj) {
return (this == obj);
}
Object 객체에서 정의하고 있는 equals는 단순히 == 연산을 통해 동일성 검사만 하고 있는 것을 확인할 수 있다.
그렇다면 제일 처음에 본 String의 equals는 어떠할까?🤔
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}
먼저 동일성 검사를 수행한 다음, 인자로 들어온 anObject가 String이라면 aString 변수로 캐스팅 되어 동등성 검사를 수행하고 있다.
해당 함수의 공식 문서를 해석하면 아래와 같은데 참고만 해 주면 된다.
Compares this string to the specified object. (이 문자열을 지정된 객체와 비교합니다.)
The result is true if and only if the argument is not null and is a String object that represents the same sequence of characters as this object. (그 결과는 인자가 null이 아니고, 이 문자열과 동일한 문자 시퀀스를 나타내는 String 객체일 경우에만 true 입니다.)
따라서 Object 타입일 때도 equals 메소드를 통해 동등성 검사를 하고 싶다면 이를 오버라이드 해 주면 된다!!
@Override
public boolean equals(Object o) {
if (this == o) // 1번
return true;
if (o == null || getClass() != o.getClass()) // 2번
return false;
Student student = (Student)o; // 3번
return name.equals(student.name);
}
- 동일성 검사를 통해 주소가 같은지 먼저 확인
- null이 거나 다른 자료형인지 확인
- 그 후 name 필드 값이 동일한지 확인 (이때 쓰이는 equals 메소드는 위에서 본 String의 equals과 동일)
System.out.println(student1.equals(student2)); // true
그러면 위에서 false가 나왔던 equals 연산은 true가 된다.
Kotlin의 동일성, 동등성
이제 kotlin 코드에서는 어떻게 동일성과 동등성을 검사하는지 알아보자.
class Student(
val name: String,
)
val student1 = Student("Bona")
val student2 = Student("Bona")
fun main() {
println(student1 == student2) // false
}
- 동일성은 === 연산을 통해 확인한다.
- 동등성은 == 연산을 통해 확인한다.
- Kotlin의 ==은 내부적으로 equals를 호출해서 객체를 비교한다.
- 따라서 위 코드에서는 내부적으로 equals를 호출하고, 별도의 equlas 함수를 오버라이드 하지 않았기 때문에 동일성 비교를 하게 된 것이다.
- 동일성 비교 → 주소가 다르기 때문에 결과는 false
따라서 동등성 비교를 하기 위해서는 Kotlin도 equals 함수를 오버라이드 해 주면 결과가 true로 나온다.
class Student(
val name: String,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true // 1번
if (other !is Student) return false // 2번
return name == other.name // 3번
}
override fun hashCode(): Int {
return name.hashCode()
}
}
val student1 = Student("Bona")
val student2 = Student("Bona")
fun main() {
println(student1 == student2) // true
}
- 동일성 검사(===연산)를 통해 주소가 같은지 먼저 확인
- 다른 자료형인지 확인
- 그 후 name 필드 값이 동일한지 확인
단, Kotlin에서는 data class를 쓰면 equals, hashCode, toString 등이 자동 구현되므로 위 Student 클래스는 아래처럼 더 간단하게 쓸 수도 있다.
data class Student(val name: String)
References
https://steady-coding.tistory.com/534