Effective Java 2 | Common Method

공부의 기록·2021년 12월 13일
0

Java (Effective Java)

목록 보기
3/12
post-thumbnail
post-custom-banner

일반적인 메서드

본 글은 2021년 12월 14일 에 기록되었다.

본 내용은 다음의 두 유형의 사람들을 전제로 하고 있다.

  1. 자바의 기본 전반을 알고 있는 사람
  2. 비교연산자 = 와 .equal() 의 차이를 아는 사람

특히 2번을 아는 사람이라면 Object 클래스의 몇몇 메서드를 오버라이드 해본 경험이 있을 것이라고 생각한다. toString 과 같은 메서드. 그런 사람이라면 더욱 이 내용을 추천한다.

일반 메서드

Object 에 있는 메서드 중 final 이 있는 메서드들은 모두 재정의를 가정하고 만들어졌고 그 항목은 다음과 같다.

  1. equals
  2. hashCode
  3. toString
  4. clone
  5. finalize

이 메서드들은 일반 규약 에 맞게 재정의하여 사용해야 한다.
말로하면 생각보다 어렵지만 코드로 적으면 생각보다 쉽다.
한 번 읽어보고 따라하면 바로 이해할 수 있을 것이라고 생각한다.


아이템


아이템 10 | equals

원제목 | equals 는 일반규약을 지켜 재정의하라

Object 명세에 적힌 일반규약은 다음과 같다.

  1. 반사성 | null 이 아닌 모든 참조값 x에 대해 x.equals(x) 는 true 다.
  2. 대칭성 | null 이 아닌 모든 참조값 x,y 에 대해 x.quals(y) 가 true 면, y.equals(x) 도 true 다.
  3. 추이성 | null 이 아닌 모든 참조값 x,y,z 에 대해 x.equals(y) 가 true 면, y.equals(z) 도 true 면, x.equals(z) 는 true 이다.
  4. 일관성 | null 이 아닌 모든 참조값 x,y 에 대해 x.equals(y) 를 반복 호출하면 항상 true 를 반환하거나 항상 false를 반환한다.
  5. null-아님 | null 이 아닌 모든 참조값 x에 대해 x.equals(null) 은 false 다.

용어 정리

  1. 논리적 동치성이란,
    쉽게 말하면 한 객체가 가진 멤버변수(혹은 멤버변수들)이 논리적으로 동일함을 의미한다.
    아래 코드를 보면 A와 B는 다른 객체이기 때문에,
    주소값을 비교하는 ==연산자는 false를 리턴하지만,
    논리적 동치성을 비교하는 equals() 메서드는 true 를 리턴하게 오버라이드 되어있다.
String A="안녕하세요";
String B="반갑습니다.";
System.out.print(A==B); // false
System.out.print(A.eqauls(B)); // true

equals 메서드 구현 방법(단계적)

  1. == 연산자를 사용해 입력이 자기 자신의 참조인지 확인한다.
    이는 성능 최적화를 위한 조치이다.
  2. instanceof 연산자로 입력의 타입을 확인하라
  3. 입력을 올바른 타입으로 형 변환하라
  4. 입력 객체와 자기 자신의 대응되는 "핵심" 필드들이 모두 일치하는지 하나씩 검사한다.

equals 메서드 구현 주의사항

  1. 입력 매개변수는 Object 타입으로 하도록 한다.
  2. 최대한 간단하게 해결하자
  3. equals 를 재정의하면 hashCode 도 재정의하자.

아이템 11 | hashCode

원제목 | equals 를 재정의하려거든 hashCode 도 재정의하라

Object 명세에 적힌 일반규약은 다음과 같다.

  1. equals 비교에 사용되는 정보(핵심 필드) 가 변경되지 않았다면,
    hashCode 메서드는 (어플리케이션을 재시작하지 않는 한) 항상 같은 값을 반환해야 한다.
  2. equals(Object)가 두 객체를 같다고 판단했다면,
    hashCode(ObjectA) 와 hashCode(ObjectB) 는 동일해야 한다.
  3. equals(Object) 가 두 객체를 다르다고 판단했더라도,
    hashCode(ObjectA) 와 hashCode(ObjectB) 가 다른 값을 반환할 필요는 없다.
    단, 다른 객체에 대해서는 다른 값을 반환하여야 해시테이블의 성능이 좋아진다.

용어정리

  1. 기본 타입 | 기본 자료형의 박싱 클래스 (Integer, Long, Float, String 등)

hashCode 메서드 구현방법(단계적)

  1. int 타입 변수 result를 선언한 후 값 c로 초기화 한다.
    이 때, c는 객체의 첫번째 핵심필드를 단계 2.a 방식으로 계산한 해시코드이다.

  2. 해당 객체의 나머지 핵심필드 f 각각에 대해 다음의 작업을 수행한다.
    2.a. 해당 필드의 해시코드 c를 계산한다.

    2.a.1. 기본 타입 필드라면 Type.hashCode(f) 를 실행한다.'

    2.a.2. 참조 타입 필드면서 이 클래스의 equals() 가 이 필드의 equals() 를 재귀적으로 호출해 비교한다면, 이 필드의 hashCode 를 재귀적으로 호출한다.
    +만약, 게산이 복잡해질 것 같으면 이 필드의 표준형을 만들어 그 표준형의 hashCode를 호출한다. 필드의 값이 null 이면 0을 사용한다.

    2.a.3. 필드가 배열이라면 핵심 원소 각각을 별도 필드처럼 다룬다.
    이상의 규칙을 재귀적으로 적용해 각 핵심 원소의 해시코드를 계산한 다음 단계 2.b 방법으로 갱신한다. 모든 원소가 핵심원소라면 Arrays.hashCode 를 사용한다.

    2.b. 단계 2.a 에서 계산한 해시코드 c 로 result 를 갱신한다.
    **result=31*result+c;

  3. result 를 반환한다.

// 클래스 중요 필드 lineNum,prefix,areaCode
@Override public int hashCode() {
    int result=Short.hashCode(areaCode);
    result=31*result+Short.hashCode(prefix);
    result=31*result+Short.hashCode(lineNum);
    return result;
}

hashCode 메서드 주의사항

  1. equals 에서 비교하지 않은 메서드는 절대로 비교하지 않는다.

hashCode 쉽게하기

  1. 성능이 중요하지 않다면 다음 Object 클래스의 정적 메서드 hash 를 사용해도 된다.
// 클래스 중요 필드 lineNum,prefix,areaCode
@Override public int hashCode() {
    return Objects.hash(lineNum,prefix,areaCode);
}

hashCode 의 충돌 횟수를 줄이기
[Guava] com.google.common.hash.Hashing 을 참고하자.


아이템 12 | toString

원제목 | toString 을 항상 재정의하라.

toString 재정의의 본질
toString 을 재정의 하는 이유는 어떤 객체에 대한 테스트 검증 절차를 쉽게 하기 위함이다.
사용자 정의 클래스 PhoneNumber 를 정의해놓고 이를 출력해보면 주소값이 나올 뿐이다.
이는 직관적이지도 않을 뿐더러 본질의 목적에 맞지 않는다.

toString 은 객체의 정보를 보여줄 수 있어야 한다.
단, 객체의 필드가 너무 많다면 핵심 필드 혹은 요약 정보를 보여줘도 된다.

toString 재정의 하지 않아도 되는 케이스
Static Utiltiy Class 는 toString 을 제공할 이유가 없다
또한 대부분의 Enum 또한 toString 을 제공할 이유가 없다.


아이템 13 | clone 주의

원제목 | clone 재정의는 주의해서 진행하라

본 내용은 Cloneable 인터페이스와 Reflextion 에 대해서 다루고 있고
이는 각각 아이템 20, 아이템 65 의 내용을 어느 정도 알아야 한다고 생각한다.

하지만 작성일 시점인 2021년 12월 14일 에는 이 부분을 공부하지 않았기에 스킵하도록 하였다.
해당 부분에 대한 학습이 필요하다는 판단이 서면, 이를 진행하고 포스트를 보완하도록 하겠다.

아이템 14 | comparable 고민

원제목 | comparable 을 구현할지 고민하라

Object.equals vs Comparable.compareTo
본 내용은 Comparable 인퍼테이스의 유일무이한 메서드인 compareTo 를 다루고 있다.
Object 클래스의 메서드인 equals 와는 두 가지만 제외하고 성격적으로 동일하다.
그 차이점은 아래와 같다.

  1. compareTo 는 순서까지 비교할 수 있다.
  2. compareTo 는 제네릭하다.

compareTo 오버라이드 이점

  1. 검색, 극단값 계산, 자동 정렬되는 컬렉션 관리 등을 쉽게 할 수 있다.

compareTo 오버라이드 안할 경우 단점

  1. 비교를 하는 클래스와 어울리지 못한다.
    +TreeSet, TreeMap,Collections,Arrays 등

Object 명세에 적힌 일반규약은 다음과 같다.

여기서부터는 compareTo 메서드에 대한 이해가 필요하다.

하지만 작성일 시점인 2021년 12월 14일 에는 이 부분을 공부하지 않았기에 스킵하도록 하였다.
해당 부분에 대한 학습이 필요하다는 판단이 서면, 이를 진행하고 포스트를 보완하도록 하겠다.

profile
2022년 12월 9일 부터 노션 페이지에서 작성을 이어가고 있습니다.
post-custom-banner

0개의 댓글