객체는 상속가능
object에서 final이 아닌 메서드(equals, hashcode, tostring, clone ,finalize)는 모두 재정의 가능함으로 규약을 맞춰서 작성해야함
equals를 재정의 하지 않는것이 좋은 경우
각 인스턴스가 본질적으로 고유하다
인스턴스의 논리적 동치성을 검사할일이 없는 경우
상위 클래스에서 재정의한 equals가 하위 클래스에도 들어맞는다
클래스가 private이거나 package-private이고 equals 메서드를 호출할 일이 없다
equals를 재 정의 해야하는 경우
객체 식별성이 아닌, 논리적 동치성을 확인하는 경우인데, 논리적 동치성이 비교가능하게 재정의 되지 않은 경우를 뜻한다(값 클래스 ex) string, integer) 이때는 반드시 일반 규약에 따라야 한다
object에서 말하는 동치 관계란?
동치 클래스 = 집합으로 서로 같은 원소들로 이뤄진 부분집합으로 나누는 연산
원칙을 따라 구현한 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가 있다.
꼭 equlas를 재정의 하지는 말자!!
equals 재정의시 hashcode도 재정의 해줘야 규약을 어기지 않는다.
여기서 2번째가 hashcode 재정의를 잘 못 했을시 문제가 된다.
좋은 hashcode 작성법
@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;
}
이 클래스는
를 반환함
→ 재정의 함으로서 사용이 간편하고 디버깅이 쉬움
최대한 보든 주요정보를 모두 반환하도록함
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을 호출 하고 필요한 필드를 전부 수정한다
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());