독서관련/Programming in Scala

Scala CH30. 객체의 동일성

ColinKang 2020. 3. 29. 13:25
아래의 글은 마틴 오더스키,렉스 스푼,빌 베너스 공저 / 오현석,이동욱,반영록 공역, 『Programming in Scala 3/e』,에이콘출판사(2017), CH01의 내용을 기반으로 작성하였습니다.

 

1. 스칼라에서의 동일성

스칼라와 자바는 동일성의 정의가 다름,  == 연산자는 값 타입에 대해서는 자연스러운 등호, 참조 타입의 경우에는 동일한 객체인지, equals 메소드는 참조 타입에 대한 표준 동일성 검사를 제공하는 사용자 정의 메소드

스칼라에사는 값 타입은 == 는 자바와 동일, 객체의 경우 == 는 자바의 equals와 동일 (동일 겍채는 x eq y), 새로운 타입에 대해 equals를 재정의하면 == 의 의미를 재정의 가능

오버라이드 하지 않은 경우는 equals는 자바의 ==와 마찬가지로 객체 동일성 사용

 

2.  동일성 비교 메소드 작성

equals 구현시 일관성이 없는 동작을 야기할 수 있는 네가지 일반적인 함정

  1. equals 선언 시 잘못된 시그니처를 사용하는 경우 : equals(other: Any) : Boolean
  2. equals 를 변경하면서 hashCode는 그대로 놔둔 경우 : 
    1. 만약 equals 메소드로 따졌을 때 두 객체가 같다면, hashCode를 각 객체에 호출한 결과도 같은 정수 값을 만들어내야만 한다
  3. equals 를 변경가능한 필드의 값을 기준으로 정의한 경우
  4. equals 를 동치 관계로 정의하지 않은 경우
    1. 반사성 : reflexive : 널이 아닌 값 x에 대해 x.equals(x) 는 true를 반환해야한다.
    2. 대칭성 : symmetric : 널이 아닌 값 x, y 에 대해 x.equals(y)가 true이면, y.equals(x) 도 true
    3. 추이성 : transitive : 널이 아닌 값 x, y, z 에 대해서 x.equals(y)가 true이고 y.equals(z) 도 true 이면 x.equals(z) 도 true를 반환
    4. 일관성 : consistent : 널이 아닌 값 x, y에 대해 x.equals(y)를 여러 번 호출해도, x, y 객체에 있는 정보가 변경되지 않는 이상 계속 true, false 중 한 값을 일관되게 반환

canEqual

LSP 위배, 에 대한 고민

 

3. 파라미터화한 타입의 동일성 정의

스칼라는 타입 소거 모델을 채택했고, 타입 파라미터는 실행 시점에 존재하지 않는다. 

와일드 카드 타입의 축약표현 : 알려지지 않은 부분이 안에 있는 타입 : Branch[_] : 패턴 매치와 메소드 호출의 타입 파라미터에 있는 밑줄은 서로 다르지만 의미는 같다, 즉 밑줄은 알려지지 않은 것을 표시

class Branch[T] {

    val elem: T,

    val left: Tree[T],

    val right: Tree[T]

} extends Tree[T] {

    override def equals(other: Any) = other match {

        case that: Branch[_] => (that canEqual this) &&

                this.elem == that.elem &&

                this.left == that.left &&

                this.right == that.right

        case _ => false

    }

    def canEqual(other: Any) = other.isInstanceOf[Branch[_]]

    override def hashCode: Int = (elem, left, right).##

}

4. equals와 hashCode 요리법