이펙티브자바 정리 2

이봐요이상해씨·2021년 12월 26일
0

목록 보기
1/6

3장 모든 객체의 공통 메서드

객체는 상속가능

object에서 final이 아닌 메서드(equals, hashcode, tostring, clone ,finalize)는 모두 재정의 가능함으로 규약을 맞춰서 작성해야함

10. equals는 일반 규약을 지켜 재정의 하라

equals를 재정의 하지 않는것이 좋은 경우

  1. 각 인스턴스가 본질적으로 고유하다

    1. 값표현이 아닌 동작 개체 표현일 경우(ex. Thread)
  2. 인스턴스의 논리적 동치성을 검사할일이 없는 경우

    1. ex)pattern클래스의 경우 정규표현식만을 검사하는거지 논리적 동치성을 검사하는것은 아니다
  3. 상위 클래스에서 재정의한 equals가 하위 클래스에도 들어맞는다

  4. 클래스가 private이거나 package-private이고 equals 메서드를 호출할 일이 없다

equals를 재 정의 해야하는 경우

객체 식별성이 아닌, 논리적 동치성을 확인하는 경우인데, 논리적 동치성이 비교가능하게 재정의 되지 않은 경우를 뜻한다(값 클래스 ex) string, integer) 이때는 반드시 일반 규약에 따라야 한다

object에서 말하는 동치 관계란?

동치 클래스 = 집합으로 서로 같은 원소들로 이뤄진 부분집합으로 나누는 연산

  1. 반사성(reflexivity): null 아닌 모든 참조값에 대해 x.equals(x)는 true이다.
    1. 객체는 자기 자신과 같아야 한다(contains메서드를 호출해서 확인할 수 있다)
  2. 대칭성(symmetry): null이 아닌 모든 참조값 x, y에 대 해 x.equals(y)가 true이면, y.equals(x)도 true이다
    1. 두 객체는 서로에 대한 동치 여부에 같은 답을 해야 한다
  3. 추이성(transitivity) : null이 아닌 모든 참조값 x, y, z에 대해 x.equals(y)가 true이고 y.equals(z)도 true이면, x.equals(z)도 true이다.
    1. 2번째 객체가 갖고, 3번째 객체 가 같으면 첫번째와 3번째도 같아야 한다
    2. 리스코프의 치환 원칙
      1. 어떤 타입에 있어 중요한 속성이라면, 그 하위 타입에서도 중요하다 띠라서 그 하위 타입의 모든 메서드가 하위타입에서 잘 작동해야 한다
    3. 상속 대신 컴포지션을 사용해라 → 추상 클래스의 하위클래스라면 equals규약을 지키면서 값을 추가할 수 있다.
  4. 일관성(consistency): null이 아닌 모든 참조값 x, y에 대해 x.equals(y)를 반복해서 호출하면 항상 true를 반환하거나 false를 반환해야한다
    1. 두 객체가 같다면 앞으로도 영원히 같아야 한다
    2. equals의 판단에 신뢰할 수 없는 자원이 끼어들게 해서는 안된다
  5. null이 아님 : null이 아닌 모든 참조값 x에 대해 x.equals(null)은 false 이다

단계별 equals 구현방법

  1. == 연산자를 사용해 입력이 자기 자신의 참조인지를 확인(자기자신이면 true반환)
  2. instanceof연산자로 입력이 올바른지 확인
  3. 입력을 올바른 타입으로 형변환
  4. 입력 객체와 자기 자신의 대응되는 핵심 필드들이 모두 일치하는지 하니씩 검사

원칙을 따라 구현한 equals

package com.springandjava.Test.effectivejava.chapter3.ex10;

public final class PhoneNumber {
	private final short areaCode, prefix, lineNum;
	
	public PhoneNumber(int areaCode, int prefix, int lineNum){
		this.areaCode = rangeCheck(areaCode, 999,"local");
		this.prefix = rangeCheck(prefix, 999, "prefix");
		this.lineNum = rangeCheck(lineNum, 9999, "linenum");
	}
	
	private static short rangeCheck(int val, int max, String arg){
		if (val <0 || val > max)
			throw new IllegalArgumentException(arg + ":"+val);
		return (short) val;
	}
	
	@Override
	public boolean equals(Object o){
		if (o == this)
			return true;
		if (!(o instanceof PhoneNumber))
			return false;
		PhoneNumber pn = (PhoneNumber)o;
		return pn.lineNum == lineNum && pn.prefix == prefix && pn.areaCode == areaCode;
	}
}

이를 대신해줄 google의 autovalue가 있다.

AutoValue

꼭 equlas를 재정의 하지는 말자!!

11. equals를 재정의 하려거든 hashcode도 재정의 해라

equals 재정의시 hashcode도 재정의 해줘야 규약을 어기지 않는다.

  1. equals 비교에 사용되는 정보가 일관하다면, hashcode메서드는 언제나 같은 값을 반환해야한다
  2. equals가 두 객체를 같다고 판단하면 두 객체의 hashcode는 똑같은 값을 반환해야한다
  3. equals가 두 객체가 다르다고 판단해도 hashcode가 서로다른 값을 반환해야할 필요는 없다

여기서 2번째가 hashcode 재정의를 잘 못 했을시 문제가 된다.

좋은 hashcode 작성법

  1. int 변수 선언
  2. 첫번째 객체의 핵심필드를 hashocde를 계산
  3. 다음 필드의 값을 해시코드 계산(재귀적 호출)
  4. 이 값을 선언한 변수로 갱신
  5. int형 변수 반환
  6. 31을 곱해주는 이유는 홀수이며넛 소수이기 때문(짝수면 오버플로우 발생)
@Override
public int hashCode(){
	int result = Short.hashCode(areaCode);
	result = 31 * result + Short.hashCode(prefix);
	result = 31 * resutl + Short.hashCode(lineNum);
}

hash메서드 이용

@Override
public int hashCode(){
	return Objects.ahsh(lineNum, prefix, areaCode);
}

쓰레드 안정성까지 고려

private int hashCode;

@Override 
public int hashCode(){
	int result = hashCode;
	if (result == 0) {
	result = Short.hashCode(areaCode);
	result = 31 * result + Short.hashCode(prefix);
	result = 31 * resutl + Short.hashCode(lineNum);
	hashCode = result;
}
return result;
}

12. toString을 항상 재정의 해라

이 클래스는

클래스_이름@16진수로 표시한 해시코드

를 반환함

→ 재정의 함으로서 사용이 간편하고 디버깅이 쉬움

최대한 보든 주요정보를 모두 반환하도록함

13. clone 재정의는 주의해서 진행해라 → 다시한번 읽어볼것!!!!

Cloneable은 복제해도 되는클래스임을 명시하는 용도의 믹스인 인터페이스

clone이란

Java - 자바 Object 클래스의 clone 메서드와 Cloneable 인터페이스에 대하여

클래스 B가 클래스 A를 상속할시, 클래스 B는 클래스 B의 객체를 반환해야 하지만, 클래스A가 자신의 생성자(new A())를 반환한다면 clone A인 클래스 B도 A타입 객체를 반환할 수 밖에 없다.

따라서 super.clone을 연쇄적 호출하도록 하면 클론이 처음 호출한 하위 클래스 객체가 만들어진다

clone 메서드는 생성자와 같은 효과이다. clone은 원본 객체에 아무런 영향을 끼치지 않음과 동시에 복제된 객체의 불변식을 보장해야한다

package com.springandjava.Test.effectivejava.chapter3.ex13;

import java.util.Arrays;
import java.util.EmptyStackException;

public class Clone1 {
	private Object[] elements;
	private int size = 0;
	private static final int DEFAULT_INTITIAL_CAPACITY = 16;

	public Clone1(){
		this.elements = new Object[DEFAULT_INTITIAL_CAPACITY];
	}

	public void push(Object e) {
		ensureCapacity();
		elements[size++] = e;
	}

	public Object pop(){
		if (size == 0)
			throw new EmptyStackException();
		Object result = elements[--size];
		elements[size] = null;
		return result;
	}

	private void ensureCapacity(){
		if (elements.length == size)
			elements = Arrays.copyOf(elements, 2*size +1);
	}
}

위와 같은 가변 상태를 참조하는 clone 메소드

@Override
public Clone1 clone(){
	try{
		Clone1 result = (Clone1) super.clone();
		result.elements = elements.clone();
		return result;
	} catch (CloneNotSupportedException e) {
		throw new AssertionError();
	}
}

Cloneable을 구현하는 모든 클래스는 Clone을 재정의해야 한다.

이때 접근 제한자는 public으로, 반환타입은 클래스 자신으로 변경한다

메서드는 가장 먼저 super.clone을 호출 하고 필요한 필드를 전부 수정한다

14. Comparable을 구현할지 고려하라

comparable이 equals와 다른점 → 단순 동치성 비교에 순서까지 비교 가능, 제네릭하다

equals 메서드는 모든 객체에 대해 전역 동치관계를 부여하는 반면, compareTo는 타입이 다른 객체는 신경쓰지 않아도된다.

public int comapreTo(phoneNumber pn){
	int result = Short.compare(areaCode, pn.areaCode);
	if (result == 0){
		result = Short.compare(prefix, pn.prefix);
		if(result == 0)
				result = Short.compare(lineNum, pn.lineNum);
		}
		return result;
}
		

연쇄적으로 comparator 인터페이스가 생성메서드를 꾸려 비교 생성 가능하게된다

private static final Comparator<PhoneNumber> COMPARATOR = 
	= comparingInt((PhoneNumber pn) -> pn.areacode)
		.thenComparingInt(pn -> pn.prefix)
		.thenComparingInt(pn -> pn.lineNum);
public int compareTo(PhoneNumber pn) {
	return COMPARATOR.compare(this.pn);
}

comparingInt는 객체 참조를 int 타입 키에 매핑되는 키함수를 인수로 받아 비교자를 반환

정적 compare메서드를 활용한 비교자

static Comparator<Object> hashCodeOrder = new Comparotr<>() {
	public int compare(Object o1, Obejcgt o2){
		return Integer.comapre(o1.hashCode(), o2.hashCode());
	}
};

비교자 생성 메서드를 활용한 비교자

static Comparotr<Object> hashcodeOrder = Comparaotr.comparingInt(o -> o.hashcode());

0개의 댓글