ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 40장 - equals 규약을 지켜라
    Kotlin/Effective Kotlin 요약 2023. 3. 29. 00:01

    Any의 규약

    • equals
    • hashCode
    • toString

    주석과 문서에 잘 설명되어 있는 규약들입니다.

    자바에서부터 정의되어 있던 메서드라서 코틀린에서 중요한 위치를 차지하고 있습니다.

    위의 규약을 위반하면 일부 객체 또는 기능이 제대로 동작하지 않을 수 있습니다.

     

    동등성

    코틀린에는 두 가지 종류의 동등성이 있습니다.

    • 구조적 동등성(equals 메서드를 기반으로 비교) , 사용하는 연산자 ==
    • 레퍼런스적 동등성(가리키는 주소를 비교) , 사용하는 연산자 ===

     

    equals가 필요한 이유

    Any 클래스에 구현되어 있는 equals는 디폴트로 ===처럼 두 인스턴스가 완전히 같은 객체인지 비교합니다.

    class Name(val name: String)
    
    fun main() {
        val name1 = Name("jun")
        val name2 = Name("jun")
        println(name1 == name2) //false
        println(name1 === name2) //false
        println(name1 == name1) //true
        println(name1 === name1) //true
    }

     

    하지만 data class로 바꾸는 순간 자동으로 equals를 구현해주어 다음과 같은 결과가 나옵니다.

    data class Name(val name: String)
    
    fun main() {
        val name1 = Name("jun")
        val name2 = Name("jun")
        println(name1 == name2) //true
        println(name1 === name2) //false
        println(name1 == name1) //true
        println(name1 === name1) //true
    }

    데이터 클래스는 내부에 어떤 값을 갖고 있는지가 중요합니다.

     

    만약 일부 필드를 기준으로 비교하고 싶다면 equals 메서드를 override 하거나 data 클래스의 생성자를 통해 활용할 수 있습니다.

    data class Member(val name: String){
        var age: Int = 0
    }
    
    fun main() {
        val name1 = Member("jun")
        val name2 = Member("jun")
        name1.age = 3
        println(name1 == name2) //true
        println(name1 === name2) //false
        println(name1 == name1) //true
        println(name1 === name1) //true
    }

    name1의 age=3으로 설정되었지만 equals에는 영향을 주지 않습니다.

     

    equals의 규약

    코틀린 1.4.31을 기준으로 equals에는 다음과 같은 주석이 달려있어야 합니다.

    구현은 반드시 다음과 같은 요구사항을 충족해야 합니다.

    • 반사적 동작
      • x가 null이 아니라면 x.equals(x)는 true를 반환해야 한다.
    • 대칭적 동작
      • x,y가 null이 아니라면 x.equals(y)는 y.equals(x)와 같은 결과를 반환해야 한다.
    • 연속적 동작
      • x, y, z가 null이 아니라면 x.equals(y)는 y.equals(z)와 같은 결과를 반환해야 한다.
    • 일관적 동작
      • x, y가 null이 아니라면 x.equals(y)는 항상 동일한 결과를 반환해야 한다.
    • null 처리
      • x가 null이 아닌 값이라면 x.equals(null)은 항상 false를 반환해야 한다.
    • equals, toString, hashCode의 동작은 매우 빠를 거라 예측되므로, 빠르게 동작해야 합니다.

    잘못 구현된 경우 확인

    contains등의 메서드가 잘 동작하는지 확인해보면 됩니다.

     

    URL과 관련된 equals 문제

    equals를 잘못 설계한 예로는 java.net.URL이 있습니다.

    IP 주소로 해석될 때는 true, 아닐 때는 false가 나옵니다.

    문제는 이 결과가 네트워크 상태에 따라 달라집니다.

     

    인터넷이 연결되어 있다면 같은 IP주소를 나타내어 true, 연결이 끊겨 있다면 false를 출력합니다.

    즉, 동작이 일관되지 않습니다.

     

    equals 구현하기

    특별한 이유가 없다면 equals를 직접 구현하지 않는 것이 좋습니다.

    데이터 클래스를 만들어서 사용하는 것이 좋습니다.

    직접 구현해야 한다면 위의 규약들을 잘 지켜서 사용해야 합니다.

    또한 final으로 사용해서 상속을 막는것이 좋습니다.

    상속을 지원하면서 완벽한 사용자 정의 equals를 함수를 만드는 것은 거의 불가능에 가깝습니다.

    댓글

Designed by Tistory.