Skip to content

Latest commit

Β 

History

History
524 lines (441 loc) Β· 28.3 KB

File metadata and controls

524 lines (441 loc) Β· 28.3 KB

equals λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”© ν• λ–„λŠ” 보편적 계약을 λ”°λ₯΄μž

  • μΈμŠ€ν„΄μŠ€μ˜ 동일여뢀λ₯Ό νŒμ •ν•˜λŠ” equals μ†Œλ“œλŠ” μ˜€λ²„λΌμ΄λ”©μ΄ κ°„λ‹¨ν•œκ²ƒ κ°™μ§€λ§Œ 잘λͺ»λ˜λŠ” κ²½μš°κ°€ γ…λ‚³μŒ

    • 문제λ₯Ό ν”Όν•˜λŠ” κ°€μž₯ μ‰¬μš΄ 방법은 equals λ©”μ†Œλ“œ(object 것)
    • λ‹€λ₯Έ μŠˆνΌν΄λž˜μŠ€μ—μ„œ object 이미 μ˜€λ²„λΌμ΄λ”© ν•œκ²ƒμ„ μ˜€λ²„λΌμ΄λ“œ ν•˜μ§€ μ•Šκ³  상속받은 κ·ΈλŒ€λ‘œ μ‚¬μš©
  • 클래슀의 각 μΈμŠ€ν„΄μŠ€κ°€ λ³Έλž˜λΆ€ν„° μœ μΌν•œκ²½μš° -μΈμŠ€ν„΄μŠ€κ°€ κ°–λŠ” κ°’λ³΄λ‹€λŠ” ν™œλ™ν•˜λŠ” κ°œμ²΄μž„μ„ λ‚˜νƒ€λ‚΄λŠ”κ²ƒμ΄ λ”μ€‘μš”ν•œ μŠ€λ ˆλ“œμ™€ 같은 ν΄λž˜μŠ€κ°€ μ—¬κΈ° ν•΄λ‹Ή

    • 그런 ν΄λž˜μŠ€λ“€μ€ μΈμŠ€ν„΄μŠ€κ°€ κ°–λŠ” 논리적인 λΉ„κ΅λŠ” μ˜λ―Έκ°€ μ—†μŒ
    • Object의 equalsλ₯Ό κ·Έλƒ₯ μ‚¬μš©ν•˜λ©΄λ¨
  • 두 μΈμŠ€ν„΄μŠ€κ°€ λ…Όλ¦¬μ μœΌλ‘œ 같은지 κ²€μ‚¬ν•˜μ§€ μ•Šμ•„λ„ λ˜λŠ” 클래슀의 경우

    • ex) java.util.Random ν΄λž˜μŠ€μœΌγ…”μ„œλŠ” λ‘κ°œμ˜ Random μΈμŠ€ν„΄μŠ€κ°€ 같은 λ‚œμˆ˜μ—΄μ„ λ§Œλ“œλŠ”μ§€ ν™•μΈν•˜κΈ° μœ„ν•΄ equals λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”© ν•  수 μžˆμ—ˆμ„ κ²ƒμ΄μ—ˆλ‹€.
    • Object의 equalsλ₯Ό κ·Έλƒ₯ μ‚¬μš©ν•˜λ©΄λ¨
  • 수퍼 ν΄λž˜μŠ€μ—μ„œ equals λ©”μ†Œλ“œλ₯Ό 이미 μ˜€λ²„λΌμ΄λ”© ν–ˆκ³ , κ·Έλ©”μ†Œλ“œλ₯Ό κ·ΈλŒ€λ‘œ μ‚¬μš©ν•΄λ„ μ’‹μ€κ²½μš°

    • Set μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” λŒ€λΆ€λΆ„μ˜ ν΄λž˜μŠ€λ“€μ€ AbstractSet 에 κ΅¬ν˜„λœ equalsλ₯Ό 상속받아 μ‚¬μš©ν•¨
    • List κ²½μš°λŠ” AbstractList μ—μ„œ μƒμ†λ°›μŒ
    • Map AbstractMap μ—μ„œ μƒμ†λ°›μŒ
  • private μ΄λ‚˜ νŒ¨ν‚€μ§€ μ „μš©(package-private) ν΄λž˜μŠ€λΌμ„œ 이클래슀의 equals λ©”μ†Œλ“œκ°€ μ ˆλŒ€ ν˜ΈμΆœλ˜μ§€ μ•Šμ•„μ•Ό ν•  경우

    • μ ‘κ·Ό μ§€μ‹œμžλ₯Ό μ§€μ •ν•˜μ§€ μ•Šμ€ λ””ν΄νŠΈ νŒ¨ν‚€μ§€ 접근을 말함
    • μ˜€λ²„λΌμ΄λ”© ν•΄μ„œ ν˜ΈμΆœλ˜μ§€ μ•Šλ„λ‘ 해야함
@Override public boolean equals(Object o){
    throws new AssertionError(); // λ©”μ†Œλ“œκ°€ μ ˆλŒ€ ν˜ΈμΆœλ˜μ§€ μ•ŠλŠ”λ‹€.
}

Object.equals λŠ” μ–Έμ œ μ˜€λ²„λΌμ΄λ“œν•΄μ•Όλ˜λ‚˜???

  • 객체 참쑰만으둜 μΈμŠ€ν„΄μŠ€ 동일 μ—¬λΆ€λ₯Ό νŒλ‹¨ν•˜λŠ”κ²ƒμ΄μ•„λ‹ˆλΌ μΈμŠ€ν„΄μŠ€κ°€ κ°–λŠ” 값을 λΉ„κ΅ν•˜μ—¬ λ…Όλ¦¬μ μœΌλ‘œ 같은지 νŒλ‹¨ν•  ν•„μš”κ°€ μžˆλŠ” 클래슀둜써
    μžμ‹ μ˜ μˆ˜νΌν΄λž˜μŠ€μ—μ„œ equlas λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œ ν•˜μ§€ μ•Šμ•˜μ„λ•Œ
  • 일반적으둜 value ν΄λž˜μŠ€κ°€ 여기에 ν•΄λ‹Ή
    • ν•˜λ‚˜μ˜ 값을 λ‚˜νƒ€λ‚΄λŠ” 클래슀 ex) Integer, Date
    • 객체 μ°Έμ‘° μ—¬λΆ€λŠ” μ€‘μš”ν•˜μ§€ μ•Šκ³  객체가 κ°–λŠ” 값이 λ…Όλ¦¬μ μœΌλ‘œ 같은지가 관심사
    • equals λ©”μ†Œλ“œμ˜ μ˜€λ²„λΌμ΄λ”©μ€ ν”„λ‘œκ·Έλž˜λ¨Έμ˜ μš•κ΅¬ μΆ©μ‘±
    • Map의 ν‚€λ‚˜ Set의 μš”μ†Œλ₯Ό 객체가 이미 μžˆλŠ”μ§€ λΉ„κ΅ν•˜λŠ” μˆ˜λ‹¨μ„ μ œκ³΅ν•΄μ•Όν•˜κΈ° λ–„λ¬Έ

equals λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œ ν•  ν•„μš”μ—†λŠ” 클래슀

  • 각 κ°’ λ‹Ή μ΅œλŒ€ν•˜λ‚˜μ˜ 객체만 μ‘΄μž¬ν•˜λ„λ‘ μΈμŠ€ν„΄μŠ€ μ œμ–΄λ₯Ό μ‚¬μš©ν•˜λŠ” 클래슀
    • μ—΄κ±°ν˜• enum
    • 그런 ν΄λž˜μŠ€λ“€μ˜ 경우 논리적인 μΌμΉ˜μ™€ 객체 μ°Έμ‘° μΌμΉ˜κ°€ λ™μΌν•œ 의미
    • Object equals λ©”μ†Œλ“œκ°€ 논리적인 equals λ©”μ†Œλ“œμ˜ κΈ°λŠ₯을 ν•˜λŠ” μ…ˆ

equals λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œ ν• λ•Œ 보편적 계약

  • μž¬κ·€μ μ΄λ‹€(Reflexive)

    • null이 μ•„λ‹Œ λͺ¨λ“  μ°Έμ‘° κ°’ x에 λŒ€ν•΄, x.equals(x) λ°˜λ“œμ‹œ true λ°˜ν™˜
  • λŒ€μΉ­μ μ΄λ‹€(Symmetric)

    • null이 μ•„λ‹Œ λͺ¨λ“  μ°Έμ‘° κ°’ x와 y에 λŒ€ν•΄ y.equals(x)κ°€ true λ°˜ν™˜ν•˜λ©΄
    • x.equals(y) 도 λ°˜λ“œμ‹œ true λ°˜ν™˜ν•΄μ•Όν•¨
  • 이행적이닀(Transitive)

    • null 이 μ•„λ‹Œ λͺ¨λ“  μ°Έμ‘° κ°’ x,y,z 에 λŒ€ν•΄ , equals λ©”μ†Œλ“œμ—μ„œ 객체 λΉ„κ΅μ‹œ μ‚¬μš©ν•˜λŠ” 정보가 λ³€κ²½λ˜μ§€ μ•ŠλŠ”λ‹€λ©΄
    • x.equals(y)λ₯Ό μ—¬λŸ¬λ²ˆ ν˜ΈμΆœν•˜λ„λ‘ μΌκ΄€μ„±μžˆκ²Œ true λ˜λŠ” falseλ₯Ό λ°˜ν™˜ν•΄μ•Όν•¨
    • null이 μ•„λ‹Œ λͺ¨λ“  μ°Έμ‘°κ°’ x에 λŒ€ν•΄ x.equals(null)은 λ°˜λ“œμ‹œ false λ°˜ν™˜ν•΄μ•Όν•¨
  • μž¬κ·€μ„±(Reflexivity)

    • 첫번쨰 μ‘°ν•­μ—λŠ” 객체λ₯Ό 자기 μžμ‹ κ³Ό λΉ„κ΅ν•˜λ©΄ κ°™μ•„μ•Όλœλ‹€κ³  λ‚˜μ™€μžˆμŒ
    • μ–΄κΈ°κΈ° 어렀움
  • λŒ€μΉ­μ„±(Symmetry)

    • 두 객체건 λŒ€μΉ­μ μœΌλ‘œ μ„œλ‘œ λΉ„κ΅ν•˜λ©΄ 같아야됨
    • μž¬κ·€μ„±κ³Ό λ‹€λ₯΄κ²Œ 무심 μ–΄κΈ°κΈ° 쉬움

예λ₯Όλ“€μ–΄ 영문 λŒ€μ†Œλ¬Έμžλ₯Ό κ΅¬λΆ„ν•˜μ§€ μ•ŠλŠ” λ¬Έμžμ—΄μ„ κ΅¬ν˜„

// 계약 깨짐 - λŒ€μΉ­μ„±μ— μœ„λ°°λ¨
public final class CaseInsensitiveString{
    private final String s;
    
    public CaseInsesitiveString(String s){
        if(s == null)
            throws new NullPointerException();
         
         this.s=s;
    
    }
    
    //계약 깨짐 - λŒ€μΉ­μ„±μ— μœ„λ°°λ¨
    @Override
    public boolean equals(Object o){
        if(o instanceof CaseInsesitivieString)
            return s.equalsIgnoreCase((CaseInsesitiveString) 0).s);
        if(o instanseof String) //ν•œμͺ½μœΌλ‘œλ§Œ μƒν˜Έ μš΄μš©λœλ‹€.
            return s.equalsIgnoreCase((String) o);
        return false;    
    }
    ...//λ‚˜λ¨Έμ§€μ½”λ“œ μƒλž΅
}
  • equals λ©”μ†Œλ“œμ—μ„œ λŒ€μ†Œλ¬Έμžλ₯Ό κ΅¬λΆ„ν•˜μ§€ μ•ŠλŠ” λ¬Έμžμ—΄(CaseInsensitiveString 객체)κ³Ό μΌλ°˜λ¬Έμžμ—΄(String 객체) λͺ¨λ‘λ₯Ό 같이 μ²˜λ¦¬ν•˜λ €κ³  ν•œλ‹€.
CaseInsesitiveString cis = new CaseInsensitiveString("Polish");
String s = "polish";
  • cis.equals(s)λŠ” true λ°˜ν™˜
  • κ·ΈλŸ¬λ‚˜ CaseInsensitiveString 의 equals λ©”μ†Œλ“œλŠ” 일반 λ¬Έμžμ—΄ μ²˜λ¦¬ν•˜κ³  μžˆλŠ” 반면
  • String 의 equals λ©”μ†Œλ“œλŠ” λŒ€μ†Œλ¬Έμžλ₯Ό κ΅¬λΆ„ν•˜μ§€ μ•ŠλŠ” λ¬Έμžμ—΄μ„ μΈμ‹ν•˜μ§€ λͺ»ν•˜λŠ” 것이 λ¬Έμ œμž„
  • λ”°λΌμ„œ s.equals(cis)λŠ” false λ°˜ν™˜
    • λŒ€μΉ­μ„±μ„ μœ„λ°˜
List<CaseInsensitiveString> list = new ArrayList<CaseInsensitiveString>();
list.add(cis);
  • 일단 equals 계약을 μ–΄κΈ°λ©΄,μš°λ¦¬κ°μ²΄μ™€ 같이 μ‚¬μš©λ˜λŠ” λ‹€λ₯Έ 객체듀이 μ–΄λ–»κ²Œ λ™μž‘ν• μ§€ μ•Œμˆ˜ μ—†μŒ

  • 문제λ₯Ό ν•΄μ†Œν•˜λ €λ©΄ equals λ©”μ†Œλ“œλ‘œ λΆ€ν„° String 객체λ₯Ό μ²˜λ¦¬ν•˜λŠ” μ½”λ“œλ₯Ό μ œκ±°ν•˜λ©΄λ¨

@Override
public boolean equals(Object o){
    return o instanseof CaseInsensitiveString &&
      ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);
}

이행성(Transitivity)

  • 첫번째 객체가 λ‘λ²ˆμ§Έ 객체와 λ™μΌν•˜κ³  λ‘λ²ˆμ§Έ 객체가 μ„Έλ²ˆμ§Έ 객체와 λ™μΌν•˜λ‹€λ©΄ 첫번째 κ°μ²΄λŠ” μ„Έλ²ˆμ§Έ 객체와 κ°™μ•„μ•Ό 함

  • κΈ°μ‘΄ 수퍼 ν΄λž˜μŠ€μ— κ°’ μ»΄ν¬λ„ŒνŠΈ(value component) Color 객체λ₯Ό μΆ”κ°€ν•˜λŠ” μ„œλΈŒ 클래슀 생각

    • equals λ©”μ†Œλ“œκ°€ 객체λ₯Ό λΉ„κ΅ν•˜λŠ”λ° ν•„μš”ν•œ 정보λ₯Ό μ„œλΈŒ ν΄λž˜μŠ€μ—μ„œ μΆ”κ°€ 제곡
  • μ •μˆ˜ μ’Œν‘œμ΄ 점을 λ‚˜νƒ€λ‚΄λŠ” λΆˆλ³€(immutable) 클래슀

public class Point{
    private final int x;
    private final int y;
    
    public Point(int x, int y){
        this.x=x;
        this.y=y;
    }
    
    @Override
    public boolean equals(Object o){
        if(!(o instanceof Point))
            return false;
        Point p =(Point)0;
        return p.x == x && p.y === y;    
    }
    
    ...// λ‚˜λ¨Έμ§€μ½”λ“œ μƒλž΅

}
  • μ λ§ˆλ‹€ 색상을 μΆ”κ°€ν•΄μ„œ λ‹€μŒκ³Ό 같은 μ„œλΈŒ 클래슀
public class ColorPoint extends Point{
    private final Color color;
    
    public ColorPoint(int x, int y, Color color){
        super(x,y);
        this.color = color;
    }
    
    // λ‚˜λ¨Έμ§€ μ½”λ“œ μƒλž΅
}
@Override
public boolean equals(Object o){
    if(!(o instanceof ColorPoint))
        return false;
    return super.equals(o) && ((ColorPoint) o).color == color;
}
  • 이 λ©”μ†Œλ“œμ— 문제점 Point 객체λ₯Ό ColorPoint 와 λΉ„κ΅ν• λ•Œ 그리고 κ·Έλ°˜λŒ€λ‘œ 비ꡐ할떄 λ‹€λ₯Έκ²°κ³Ό λ‚˜μ˜΄
  • Point 객체λ₯Ό ColorPoint λΉ„κ΅ν• λ•ŒλŠ” 색상이(Color) λΉ„κ΅μ—μ„œ 빠짐 λ°˜λŒ€λ‘œ λΉ„κ΅ν• λ–„λŠ” 항상 false λ°˜ν™˜
    • μΈμžκ°’μ΄ 틀리기 떄문에
Point p = new Point(1,2);
ColorPoint cp = new ColorPoint(1,2, Color.RED);
  • 이경우 p.equals(cp)λŠ” true 반면 cp.equals(p)λŠ” false
// 계약 깨짐 - 이행성에 μœ„λ°°λœλ‹€
@Override
public boolean equals(Object o){
    if(!(o instanseof Point))
        return false;
        
     // 만일 oκ°€ Point 객체라면 Colorλ₯Ό λΉΌκ³  λΉ„κ΅ν•œλ‹€.
     if(!(o instanceof ColorPoint))
        return o.equals(this);
        
      // oκ°€ ColorPoint 객체라면, Point와 Color λͺ¨λ‘ λΉ„κ΅ν•œλ‹€.
      return super.equals(o) && ((ColorPoint)o).color == color;
}
  • Point 객체λ₯Ό λΉ„κ΅ν• λ•ŒλŠ” 색상(Color)λ₯Ό λ¬΄μ‹œν•˜λ„λ‘ λ‹€μŒκ³Ό 같이 ColorPoint.equals μˆ˜μ •ν•˜μ—¬ 문제 ν•΄κ²°ν• μˆ˜μžˆμ§€λ§Œ λ¬Έμ œκ°€μƒκΉ€
    • 이행성에 λŒ€ν•œ 문제
ColorPoint p1 = new ColorPoint(1,2,Color.RED);
Point p2 = new Point(1,2);
ColorPoint p3 = new ColorPoint(1,2,Color.BLUE);
  • p1.equals(p2), p2.equals(p3) true λ°˜ν™˜

  • p1.equals(p3)λŠ” false λ°˜ν™˜

    • λͺ…λ°±ν•œ 이행성 μœ„λ°˜
  • μΈμŠ€ν„΄μŠ€ 생성이 κ°€λŠ₯ν•œ 클래슀의 μ„œλΈŒ 클래슀 κ°’ μ»΄ν¬λ„ŒνŠΈλ₯Ό μΆ”κ°€ν•˜λ©΄μ„œ equals 계약을 지킬 수 μž‡λŠ” 방법은 μ—†μŒ

// λ¦¬μŠ€μ½”ν”„(Liskov)의 λŒ€μ²΄μ›μΉ™(substitution principle)에 μœ„λ°°λœλ‹€.
@Override public boolean equals(Object o){
    if(o == null || o.getClass() != getClass())
        return false;
     Point p = (Point) o;
     return p.x == x && p.y ==y;
}
  • μœ„ 경우 λΉ„κ΅ν•˜λŠ” λ‘κ°μ²΄μ˜ 클래슀만 κ°™μ„λ–„λ§Œ 효과 있음

  • μ •μˆ˜μ˜ μ’Œν‘œ 값을 κ°–λŠ” 점이 λ‹¨μœ„μ›(Unit Circle) 상에 μžˆλŠ”μ§€ μ•Œλ €μ£ΌλŠ” λ©”μ†Œλ“œμž‘μ„±

// λ‹¨μœ„μ› μƒμ˜ λͺ¨λ“  μ ‘λ―ˆ ν¬ν•¨ν•˜λ„λ‘ UnitCircle μ΄ˆκΈ°ν™” ν•œλ‹€.
private static final Set<Point> unitCicle;
static {
    unitCircle = new HashSet<Point>();
    unitCircle.add(new Point(1,0));
    unitCircle.add(new Point(0,1));
    unitCircle.add(new Point(-1,0));
    unitCircle.add(new Point(0,-1));
}

public static boolean onUnitCircle(Point p){
    return unitCircle.contains(p);
}
  • κ·ΈλŸ¬λ‚˜ κ°’ μ»΄ν¬λ„ŒνŠΈλ₯Ό μΆ”κ°€ν•˜μ§€ μ•ŠλŠ” 일반적인 λ°©λ²•μœΌλ‘œ Point μ„œλΈŒ 클래슀λ₯Ό λ§Œλ“ λ‹€κ³  해보면 μ–Όλ§ˆλ‚˜ λ§Žμ€ μΈμŠ€ν„΄μŠ€κ°€ μƒμ„±λ˜μ—ˆλŠ”μ§€ κΈ°μ–΅ν•˜λŠ” μƒμ„±μžλ₯Ό κ°–λŠ”κ²ƒμ΄λ‹€.
public class CounterPoint extends Point{
    private static final AtomicInteger counter = new AtomicInteger();
    
    public CounterPoint(int x, int y){
        super(x,y);
        counter.incrementAndGet();
    }
    
    public int numberCreated() { return counter.get(); }
}
  • νŠΉμ • νƒ€μž…μ˜ μ–΄λ–€ λ©”μ†Œλ“œκ±΄ μžμ‹ μ˜ μ„œλΈŒ νƒ€μž…μ—μ„œλ„ λ˜‘κ°™μ΄ λ™μž‘ν•˜κΈ° μœ„ν•΄μ„œ κ·Ένƒ€μž…μ˜ μ–΄λ–€ μ€‘μš”ν•œ 속성도 μžμ‹ μ˜ μ„œλΈŒνƒ€μž…μ„ μœ„ν•΄ κ°–κ³ μžˆμ–΄μ•Όλœλ‹€κ³  λ¦¬μŠ€μ½”ν”„μ˜ λŒ€μ²΄μ›μΉ™(Liskov substitution principle에 λ‚˜μ™€μžˆμŒ

  • CounterPoint μΈμŠ€ν„΄μŠ€λ₯Ό onUnitCircle λ©”μ†Œλ“œ 인자둜 μ „λ‹¬ν•œλ‹€κ³  κ°€μ •

  • 만일 Point ν΄λž˜μŠ€μ—μ„œ getClass ν˜•νƒœμ˜ equals λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•œλ‹€λ©΄, CounterPoint μΈμŠ€ν„΄μŠ€μΈ x와 y 관계없이 onUnitCircle λ©”μ†Œλ“œμ—μ„œ falseλ₯Ό λ°˜ν™˜ν• κ²ƒμž„

    • κ·Έ μ΄μœ λŠ” onUnitCircle λ©”μ†Œλ“œμ—μ„œ μ‚¬μš©ν•˜λŠ” HashSetκ³Ό 같은 μ»¬λ ‰μ…˜μ—μ„œ μ €μž₯된 객체인지 μ—¬λΆ€λ₯Ό ν™•μΈν•˜κΈ° μœ„ν•΄ equals λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ”λ° CounterPoint μΈμŠ€ν„΄μŠ€λŠ” μ–΄λ–€ Point μΈμŠ€ν„΄μŠ€μ™€λ„ κ°™μ§€μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€.
  • Point ν΄λž˜μŠ€μ—μ„œ instanceof ν˜•νƒœμ˜ equals λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•œλ‹€λ©΄, CounterPoint μΈμŠ€ν„΄μŠ€λ₯Ό 인자둜 주어도 onUnitCircle λ©”μ†Œλ“œλŠ” 잘 λ™μž‘ ν•  것이닀.

  • 상속(inheritance)λ³΄λ‹€λŠ” μ»΄ν¬μ§€μ…˜(composition)을 μ‚¬μš©ν•˜μžλŠ” ꢌ고λ₯Ό λ”°λ₯΄λŠ” 것

  • ColorPoint λ₯Ό Point μ„œλΈŒ 클래슀둜 λ§Œλ“œλŠ”λŒ€μ‹  private Point ν•„λ“œμ™€ public λ·° λ©”μ†Œλ“œλ₯Ό ColorPoint ν΄λž˜μŠ€μ— μΆ”κ°€ν•˜λŠ”κ²ƒ

  • λ·° λ©”μ†Œλ“œλŠ” ColorPoint μΈμŠ€ν„΄μŠ€μ™€ 같은 μ’Œν‘œ μœ„μΉ˜λ₯Ό κ°–λŠ” Point μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€.

// equals 계약을 μ–΄κΈ°μ§€ μ•ŠμœΌλ©΄μ„œ κ°’ μ»΄ν¬λ„ŒνŠΈλ₯Ό μΆ”κ°€ν•œλ‹€.
public class ColorPoint{
    private final Point point;
    private final Color color;
    
    public ColorPoint(int x, int y, Color color){
        if(color ==null)
            throw new NullPointerException();
         point = new Point(x,y);
         this.color = color;
    }
    
    /**
     * ColorPoint μΈμŠ€ν„΄μŠ€μ™€ 같은 μ’Œν‘œλ₯Ό κ°–λŠ” Point μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€,
     */
     public Point asPoint(){
        return point;
     }
     
     @Override
     public boolean equals(Object o){
        if(!(o instanceof ColorPoint))
            return false;
         ColorPoint cp = (ColorPoint) o;
         return cp.point.equals(point) && cp.color.equals(color);   
     }
     
     ..// λ‚˜λ¨Έμ§€ μ½”λ“œλŠ” μƒλž΅
}
  • μΈμŠ€ν„΄μŠ€ 생성이 κ°€λŠ₯ν•œ 클래슀둜 λΆ€ν„° 상속받아 κ°’ μ»΄ν¬λ„ŒνŠΈλ₯Ό μΆ”κ°€ν•˜κ³  μž‡λŠ” ν΄λž˜μŠ€λ“€μ΄ μ‹€μ œλ‘œ μžλ°” ν”Œλž«νΌμ— μžˆλ‹€.
  • 좔상(abstract) 클래슀의 μ„œλΈŒ ν΄λž˜μŠ€μ—μ„œλŠ” equals 계약을 μœ„λ°°ν•˜μ§€ μ•Šκ³  μ»΄ν¬λ„ŒνŠΈλ₯Ό μΆ”κ°€ ν•  μˆ˜μž‡λ‹€λŠ”κ²ƒμ— μ£Όλͺ©
  • 예λ₯Ό λ“€μ–΄ κ°’ μ»΄ν¬λ„ŒνŠΈκ°€ μ—†λŠ” Shape λΌλŠ” μΆ”μƒν΄λž˜μŠ€κ°€ μž‡κ³  그것을 μ„œλΈŒ 클래슀둜 Circle, Rectangle 이 μžˆμ„ 수 있음
    • 이 경우 μœ„μ™€κ°™μ€ 문제점 생기지 μ•ŠμŒ 수퍼클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό 직접 μƒμ„±ν• μˆ˜ μ—†κΈ°λ•Œλ¬Έ
  • 일관성(Consistency) 만일 두객체가 λ™μΌν•˜λ‹€λ©΄, ν•˜λ‚˜κ°€ λ³€κ²½λ˜μ§€ μ•ŠλŠ” ν•œ 항상 동일함을 μœ μ§€ν•΄μ•Ό ν•œλ‹€λŠ”κ²ƒμ΄ equals λ„€λ²ˆμ§Έ μ‘°ν•­
    • 즉 κ°€λ³€ 객체듀은 μ‹œμ μ΄ 달라져도 λ‹€λ₯Έ 객체듀과 동일 ν•  수 μžˆμ§€λ§Œ λΆˆλ³€κ°μ²΄λ“€μ€ 그럴 수 μ—†μŒ
    • λΆˆλ³€ 클래슀둜 λ§Œλ“€μ–΄μ•Ό λœλ‹€λ©΄ λ™μΌν•œ 객체듀은 κΎΈμ€€νžˆ 동일함을 μœ μ§€ν•˜κ³  λ™μΌν•˜μ§€ μ•ŠλŠ” 객체듀은 쀄곧 λ™μΌν•˜μ§€ μ•ŠμŒμ„ μœ μ§€ν•˜λ„λ‘ equals λ©”μ†Œλ“œλ₯΄ μž‘μ„±
  • ν΄λž˜μŠ€κ°€ λΆˆλ³€μ΄κ±΄ μ•„λ‹ˆκ±΄ μ‹ λ’°ν•  수 μ—†λŠ” μžμ›μ— μ˜μ‘΄ν•˜λŠ” equals λ©”μ†Œλ“œλ₯Ό μž‘μ„±ν•˜μ§€λ§μž
  • Null μ•„λ‹˜ λ©”μ†Œλ“œκ°€ κ΅¬ν˜„ν•˜λŠ” 동등관계 λ§ˆμ§€λ§‰ μš”κ΅¬μ‚¬ν•­ λͺ¨λ“  κ°μ²΄λŠ” nullκ³Ό κ°™μ§€ μ•Šμ•„μ•Ό ν•œλ‹€λŠ” 의미
@Override public boolean equals(Object o){
    if(o == null)
      return false;
}
  • μœ„μ™€ 같은 검사 λΆˆν•„μš”ν•¨ equals λ©”μ†Œλ“œμ—μ„œλŠ” μš°μ„  비ꡐ에 μ ν•©ν•œ νƒ€μž…μœΌλ‘œ 인자λ₯Ό λ°˜ν™˜ν•΄μ•Ό ν•˜λ©° μ–΄μ°¨ν”Ό null이 걸러짐
    • νƒ€μž…μ„ λ°˜ν™˜ν•˜λŠ” μ΄μœ λŠ” 객체λ₯Ό λΉ„κ΅ν• λ•Œ κ·ΈμΈμžκ°€ μ°Έμ‘°ν•˜λŠ” 객체의 λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜κ±°λ‚˜ ν•„λ“œλ₯Ό μ ‘κ·Ό ν•  수 μžˆμ–΄μ•Ό ν•˜κΈ° λ•Œλ¬Έ
    • μ „λ‹¬λœ 인자의 νƒ€μž…μ΄ Object dml 수퍼 클래슀 νƒ€μž…μœΌλ‘œ μžλ™μ—…μΊμŠ€νŒ… λ˜λ―€λ‘œ equals λ©”μ†Œλ“œ λ‚΄λΆ€μ—μ„œ 이 인자λ₯Ό μ˜¬λ°”λ₯΄κ²Œ μ‚¬μš©ν•˜λ €λ©΄ μ ν•©ν•œ μ„œλΈŒν΄λž˜μŠ€ νƒ€μž…μœΌλ‘œ λ³€ν™˜(λ‹€μš΄μΊμŠ€νŒ…) 해야함
@Override
public boolean equals(Object o){
    if(!(o instanceof MyType))
        return false;
    myType mt = (MyType) o;
    ...
}
  • νƒ€μž… 검사λ₯Ό ν•˜μ§€ μ•Šμ•˜λŠ”λ° 잘λͺ»λœ νƒ€μž…μ˜ μΈμžκ°€ equals λ©”μ†Œλ“œλ‘œ μ „λ‹¬λ˜λ©΄ ClassCastException μ˜ˆμ™Έκ°€ λ°œμƒ
  • equals 계약을 μœ„λ°˜ν•˜κ²Œλ¨
  • instanceof μ—°μ‚°μžλ‘œ κ²€μ‚¬ν•˜λ©΄, λ‘λ²ˆμ§Έ ν”Όμ—°μ‚°μž(였λ₯Έμͺ½) νƒ€μž…κ³Ό 상관없이 ν”Όμ—°μ‚°μž(μ™Όμͺ½) null인 경우 false κ°€ λ°˜ν™˜λœλ‹€.
  • null 이 인자둜 μ „λ‹¬λ˜λ©΄ false κ°€ λ°˜ν™˜λ˜λ―€λ‘œ λ”°λ‘œ null을 검사 ν•  ν•„μš”κ°€ μ—†μŒ

μ–‘μ§ˆμ˜ equals λ©”μ†Œλ“œλ₯Ό λ§Œλ“œλŠ”λ²•

  1. 객체의 값을 비ꡐ ν•  ν•„μš” μ—†κ³  참쑰만으둜 같은 객체인지 비ꡐ κ°€λŠ₯ν•˜λ‹€λ©΄ == μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•˜μž. 그리고 κ°™λ‹€λ©΄ true λ₯Ό λ°˜ν™˜ν•¨ 이것은 μ½”λ“œ μ„±λŠ₯을 μ΅œμ ν™” ν•˜λŠ” 것에 λΆˆκ³Όν•˜μ§€λ§Œ, 만일 λΉ„κ΅ν•˜λŠ” λΉ„μš©μ΄ μ—„μ²­ 많이 λ“ λ‹€λ©΄ κ·ΈλŸ¬κ²Œν•  κ°€μΉ˜κ°€ 있음

2.instanceof μ—°μ‚°μžλ₯Ό μ‚¬μš©ν•΄μ„œ μ „λ‹¬λœ μΈμžκ°€ μ˜¬λ°”λ₯Έ νƒ€μž…μΈμ§€ ν™•μΈν•˜μž.

  • 만일 κ·Έλ ‡μ§€ μ•Šλ‹€λ©΄ false λ₯Ό λ°˜ν™˜ν•œλ‹€. λŒ€κ°œμ˜ 경우 μ˜¬λ°”λ₯Έ νƒ€μž…μ΄λž€ 호좜된 equals λ©”μ†Œλ“œκ°€ μ •μ˜λœ 클래슀λ₯Ό λ§ν•˜μ§€λ§Œ κ·Έ ν΄λž˜μŠ€κ°€ κ΅¬ν˜„ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€λ„ μ˜¬λ°”λ₯Έ νƒ€μž…μ΄ 될수 μžˆλ‹€.
  • λ”°λΌμ„œ μ–΄λ–€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” ν΄λž˜μŠ€λ“€μ΄ μ—¬λŸ¬κ°œ μž‡κ³  각 ν΄λž˜μŠ€λ“€μ€ equals 계약에 맞게 equals λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”© ν•˜κ³  μžˆλ‹€ κ·Έ μΈν„°νŽ˜μ΄μŠ€λ„ μ˜¬λ°”λ₯Έ νƒ€μž…μ΄ 될수 있음
  • μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λŠ” ν΄λž˜μŠ€λ“€μ΄ μ—¬λŸ¬κ°œ 있고 κ°ν΄λž˜μŠ€λ“€μ€ equals 계약에 맞게 equals λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ”© ν•˜κ³ μžˆλ‹€λ©΄ κ·Έ μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μœΌλ‘œ 각 ν΄λž˜μŠ€λ“€μ˜ 객체λ₯Ό 비ꡐ ν•  수 있음
    • ex) Set,List,Map, Map.Entry 같은 μ»¬λ ‰μ…˜ μΈν„°νŽ˜μ΄μŠ€κ°€ κ·ΈλŸ°ν˜•νƒœ
  1. μ•ˆμž νƒ€μž…μ„ μ˜¬λ°”λ₯Έ νƒ€μž…μœΌλ‘œ λ³€ν™˜ν•œλ‹€. 이런 νƒ€μž… λ³€ν™˜μ€ instanceof 검사후 ν–‰ν•˜κ²Œ λ˜λ―€λ‘œ μ˜ˆμ™Έκ°€ μƒκΈ°μ§€μ•Šκ³  μ•ˆμ „ν•˜κ²Œ μ²˜λ¦¬λœλ‹€.

  2. 클래슀의 μ€‘μš”ν•œ(곑 비ꡐ해야 ν•˜λŠ”)ν•„λ“œ 각각에 λŒ€ν•΄μ„œλŠ” 인자둜 μ „λ‹¬λœ 객체의 ν•„λ“œμ™€ ν˜„μž¬ 객체의 ν•„λ“œκ°€ λͺ¨λ‘ 같은지 λΉ νŠΈλ¦¬μ§€ 말고 λΉ„κ΅ν•œλ‹€.

  • λͺ¨λ‘ κ°™λ‹€λ©΄ true, κ·Έλ ‡μ§€ μ•Šλ‹€λ©΄ false λ₯Ό λ°˜ν™˜
  • 만일 μ•žμ˜ 2λ²ˆμ—μ„œ νƒ€μž…κ²€μ‚¬μ‹œ 비ꡐ할 객체의 νƒ€μž…μ΄ μΈν„°νŽ˜μ΄μŠ€μΌλ•ŒλŠ” κ·Έ μΈν„°νŽ˜μ΄μŠ€μ˜ λ©”μ†Œλ“œλ₯Ό ν†΅ν•΄μ„œ 인자의 ν•„λ“œλ₯Ό μ ‘κ·Όν•˜λ©°, 비ꡐ할 객체의 νƒ€μž…μ΄ 클래슀라면 ν•„λ“œλ₯Ό λ°”λ‘œ μ ‘κ·Ό ν•  수 μžˆλ‹€.
  • λ¬Όλ‘  ν•΄λ‹Ή ν•„λ“œμ˜ μ ‘κ·Ό λ²”μœ„κ°€ μ–΄λ–»κ²Œ μ§€μ •λ˜μ—ˆλŠ”κ°€μ— 따라 닀름
(field == o.field || (field ! =null && field.equals(o.field))
  • 이런 κ²½μš°μ—λŠ” ν•„λ“œμ˜ ν‘œμ€€ν˜•μ‹(canonical form)을 μœ μ§€ν•˜μ—¬ equals λ©”μ†Œλ“œμ—μ„œ 그것을 κΈ°μ€€μœΌλ‘œ ν•„λ“œ κ°’μ˜ 동일 μ—¬λΆ€λ₯Ό λΉ„κ΅ν•œλ‹€λ©΄ λΉ„μš©μ΄ 적게 λ“œλŠ” 비ꡐλ₯Ό ν•  수 있음
  • 이 기법은 λΆˆλ³€ 클래슀 객체λ₯Ό λΉ„κ΅ν• λ•Œ κ°€μž₯μ ν•©ν•˜λ‹€.
  • λ§Œμ•½ 객체의 값이 λ³€κ²½ 될 수 μžˆλ‹€λ©΄ ν‘œμ€€ ν˜•μ‹μ„ μ΅œμ‹ μœΌλ‘œ μœ μ§€ν•΄μ•Όν•œλ‹€.
  • equals λ©”μ†Œλ“œμ˜ μ„±λŠ₯은 λΉ„κ΅ν•˜λŠ” ν•„λ“œμ˜ μˆœμ„œμ— 영ν–₯을 받을 수 μžˆλ‹€.
    • μ„±λŠ₯을 κ°€μž₯ μ’‹κ²Œ ν•˜λ €λ©΄ λ‹€λ₯Ό κ°€λŠ₯성이 λ§Žκ±°λ‚˜ λΉ„κ΅λΉ„μš©μ΄ μ κ²Œλ“œλŠ” ν•„λ“œλΆ€ν„° λ¨Όμ € 비ꡐ
    • 객체의 동기화(synchronizeation)에 μ‚¬μš©λ˜λŠ” 락(lock) ν•„λ“œμ²˜λŸΌ 객체 μžμ‹ μ΄ κ°–μ§€ μ•ŠλŠ” ν•„λ“œλ₯Ό λΉ„κ΅ν•˜λ©΄ μ•ˆλœλ‹€.
    • 비ꡐλ₯Ό ν•΄μ•Όν•˜λŠ” μ€‘μš”ν•„λ“œμ˜ 값을 μ—°μ‚°μ²˜λ¦¬ν•˜μ—¬ μ–»μ–΄μ§€λŠ” νŒŒμƒ ν•„λ“œλŠ” 비ꡐ할 ν•„μš” μ—†λ‹€.
    • κ·ΈλŸ¬λ‚˜ νŒŒμƒ ν•„λ“œκ°€ κ·Έ 객체 전체λ₯Ό μš”μ•½ν•΄μ„œ λ‚˜νƒ€λ‚΄λŠ” μ—­ν•  이라면 각 ν•„λ“œμ˜ 값을 λΉ„κ΅ν•˜λŠ”κ²ƒλ³΄λ‹€ νŒŒμƒ ν•„λ“œλ₯Ό λΉ„κ΅ν•˜λŠ”κ²ƒμ΄ μ„±λŠ₯ ν–₯상에 도움이 될것이닀.
    • λ‹€κ°ν˜•(Polygon) ν΄λž˜μŠ€κ°€ 있고 두 개의 λ‹€κ°ν˜• κ°μ²΄λ“€μ˜ 면적을 νŒŒμƒν•„λ“œλ‘œ κ°–κ³  λΉ„κ΅ν•œλ‹€λ©΄ 각 λ‹€κ°ν˜•μ˜ λ³€κ³Ό 꼭지점을 λͺ¨λ‘ 비ꡐ ν•  ν•„μš”κ°€ μ—†μŒ
  1. equals λ©”μ†Œλ“œλ₯Ό μž‘μ„±ν•œ 후에 κ³Όμ—° κ·Έλ©”μ†Œλ“œκ°€ λŒ€μΉ­μ μ΄λ©° 이행적이고, 일관성 μžˆλŠ”μ§€λ₯Ό 확인함.
  • λ‹¨μœ„ ν…ŒμŠ€νŠΈ μ½”λ“œλ₯Ό μž‘μ„±ν•˜μ—¬ 그런 속성듀이 μ§€μΌœμ§€λŠ”μ§€ κ²€μ‚¬ν•œλ‹€.
  • 만일 μ§€μΌœμ§€μ§€ μ•ŠλŠ”λ‹€λ©΄ equals λ©”μ†Œλ“œλ₯Ό μˆ˜μ •ν•œλ‹€. λ¬Όλ‘  λ‹€λ₯Έ 두가지 속성(μž¬κ·€μ„±κ³Ό null이 μ•„λ‹˜ 도 λ§Œμ‘±ν•΄μ•Όν•¨)

μœ μ˜ν•  사항

  • equals λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œν• λ•Œ hashCode λ©”μ†Œλ“œλ„ 항상 μ˜€λ²„λΌμ΄λ“œ ν•œλ‹€.
  • λ„ˆλ¬΄ λ˜‘λ˜‘ν•œμ²™ ν•˜μ§€λ§ˆλΌ λ‹¨μˆœνžˆ ν•„λ“œ 값이 같은지 κ²€μ‚¬ν•˜λŠ” 것이라면 equals 계약 μ€€μˆ˜κ°€ μ–΄λ ΅μ§€ γ…‡λ‚³λ‹€.
    • ν•˜μ§€λ§Œ λ„ˆλ¬΄ μ§€λ‚˜μΉ˜κ²Œ 동일여뢀λ₯Ό λΉ„κ΅ν•˜λ €λ©΄ λ¬Έμ œκ°€ 생기기 쉽닀.
    • 앨리어싱 (λ‘κ°œ μ΄μƒμ˜ 객체 μ°Έμ‘°κ°€ λ™μΌν•œ 객체λ₯Ό κ°€λ¦¬ν‚€λŠ”κ²ƒμ„ 앨리어싱이라고함) 된 κ°μ²΄λ“€μ˜ λΉ„κ΅κΉŒμ§€λ„ κ³ λ €ν•˜λŠ”κ²ƒμ€ 쒋은생각이 μ•„λ‹˜
  • equals λ©”μ†Œλ“œ 인자 νƒ€μž…μ„ Object λŒ€μ‹  λ‹€λ₯Έ νƒ€μž…μœΌλ‘œ λ°”κΎΈμ§€ 말자.
public boolean equals(MyClass o){
 ...
}
  • μ—¬κΈ°μ„œλŠ” λ©”μ†Œλ“œ 인자 Object νƒ€μž…μΈ Object.equalsλ₯Ό μ˜€λ²„λΌμ΄λ“œ(override) ν•œκ²ƒμ΄ μ•„λ‹ˆλΌ μ˜€λ²„λ‘œλ“œ(overload) ν•˜κ³  μžˆλ‹€λŠ” 것이 λ¬Έμ œμ΄λ‹€.
  • 두 λ©”μ†Œλ“œμ˜ λ°˜ν™˜νƒ€μž…μ΄ κ°™λ‹€λ©΄ ν‰λ²”ν•œ equals λ©”μ†Œλ“œμ— λΆ€κ°€ν•˜μ—¬ λ˜λ‹€λ₯Έ equals(인자의 κ°œμˆ˜λ‚˜ νƒ€μž…μ΄ λ‹€λ₯Έ) λ©”μ†Œλ“œλ₯Ό μ •μ˜(μ˜€λ²„λ‘œλ“œ)해도 λ¬Έμ œμ—†λ‹€.
  • κ·ΈλŸ¬λ‚˜ μ—¬κΈ°μ„œλŠ” κ·Έλž˜μ•Όν•  μ΄μœ κ°€μ—†μŒ μ˜€λ²„λΌμ΄λ”© 해야할것을 잘λͺ»ν•΄μ„œ μ˜€λ²„λ‘œλ”© ν•œκ²ƒμ΄κΈ° 떄문에
  • @Override 주석을 μΌκ΄€λ˜κ²Œ μ‚¬μš©ν•˜λ©΄ 이런 μ‹€μˆ˜λ₯Ό λ§‰μ•„μ€Œ λ‹€μŒκ³Ό 같은 equals λ©”μ†Œλ“œλŠ” μ»΄νŒŒμΌμ—λŸ¬ 생길것이고 κ·Έμ΄μœ λŠ” μ—λŸ¬λ©”μ„Έμ§€μ—μ„œ μž˜μ•Œλ €μ€Œ
@Override public boolean equals(MyClass o){
....
}

equals λ©”μ†Œλ“œ μ˜€λ²„λΌμ΄λ“œν• λ–„λŠ” hashCode λ©”μ†Œλ“œλ„ 항상 같이 μ˜€λ²„λΌμ΄λ“œν•˜μž

  • equals λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œ ν•˜λŠ” λͺ¨λ“  ν΄λž˜μŠ€μ—μ„œλŠ” λ°˜λ“œμ‹œ hashCode λ©”μ†Œλ“œλ„ μ˜€λ²„λΌμ΄λ“œ 해야함
  • κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ Object.hashCode λ©”μ†Œλ“œμ˜ 보편적 계약을 μœ„λ°˜ν•˜κ²Œ λ˜λ―€λ‘œ HashMap,HashSet,Hashtable 을 ν¬ν•¨ν•˜λŠ” λͺ¨λ“  ν•΄μ‹œ 기반의 μ»¬λ ‰μ…˜λ“€κ³Ό 우리 클래슀λ₯Ό 같이 μ‚¬μš©ν•  경우 μ˜¬λ°”λ₯΄κ²Œ λ™μž‘ν•˜μ§€ μ•Šμ„ 것이닀.

μžλ°” API λ¬Έμ„œμ˜ Object.hashCode λͺ…μ„Έ

  • μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜ 싀행쀑에 같은 객체에 λŒ€ν•΄ ν•œλ²ˆ 이상 ν˜ΈμΆœλ˜λ”λΌλ„ hashCode λ©”μ†Œλ“œλŠ” 같은 μ •μˆ˜λ₯Ό 일관성 있게 λ°˜ν™˜ν•΄μ•Ό ν•œλ‹€.

  • equals(Object) λ©”μ†Œλ“œ 호좜 κ²°κ³Ό 두객체가 λ™μΌν•˜λ‹€λ©΄, 두 객체 각각에 λŒ€ν•΄ hashCode λ©”μ†Œλ“œλ₯Ό 호좜 ν–ˆμ„λ•Œ 같은 μ •μˆ˜ 값이 λ‚˜μ™€μ•Ό ν•œλ‹€.

  • equals(Object) λ©”μ†Œλ“œ 호좜 κ²°κ³Ό 두 객체가 λ‹€λ₯΄λ‹€κ³  ν•΄μ„œ 두객체 각각에 λŒ€ν•΄ hashCode λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν–ˆμ„λ•Œ λ°˜λ“œμ‹œ λ‹€λ₯Έ μ •μˆ˜ 값이 λ‚˜μ˜¬ ν•„μš”λŠ” μ—†μŒ

    • κ·ΈλŸ¬λ‚˜ 같이 μ•Šμ€ 객체듀에 λŒ€ν•΄ hashCode λ©”μ†Œλ“œμ—μ„œ μ„œλ‘œλ‹€λ₯Έ μ •μˆ˜κ°’μ„ λ°˜ν™˜ν•˜λ©΄, 이 λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” ν•΄μ‹œμ»¬λ ‰μ…˜λ“€(HashMap,HashSet,Hashtable) 등이 μ„±λŠ₯을 ν–₯상 μ‹œν‚¬μˆ˜ μžˆμŒμ„ μ•Œμ•„μ•Ό ν•œλ‹€.
  • hashCode 의 μ˜€λ²„λΌμ΄λ”©μ— μ‹€νŒ¨ ν–ˆμ„λ–„ μœ„λ°°λ˜λŠ” μ£Όμš” 쑰항은 λ‘λ²ˆμ§Έ κ²ƒμœΌλ‘œμ¨ λ™μΌν•œ 객체듀은 같은 ν•΄μ‹œμ½”λ“œλ₯Ό 값을 κ°€μ Έμ•Ό ν•œλ‹€λŠ”κ²ƒ

public final class PhoneNumber{
 private final short areaCode;
 private final short prefix;
 private final short lineNumber;
 
 public PhoneNumber(int areaCode, int prefix, int lineNumber){
    rangeCheck(areaCode, 999 , "area code");
    rangeCheck(prefix, 999 , "prefix");
    rangeCheck(lineNumber, 9999, "line number");
    this.areaCode = (short) areaCode;
    this.prefix = (short) prefix;
    this.lineNumber = (short) lineNumber;
 
 }
 
 private static void rangeCheck(int arg, int max, String name){
     if(arg < 0 || arg > max)
     throw new IllegalArgumentException(name + ": "+arg);
 }
 
 @Override
 public boolean equals(Object o){
  if(o == this)
  return true;
  if(!(o instanceof PhoneNumber))
  return false;
  PhoneNumber pn = (PhoneNumber)o;
  return pn.lineNumber = lineNumber
   && pn.prefix=prefix
   && pn.areaCode = areaCode;
 }
  
  // 계약 깨짐 - hashCode λ©”μ†Œλ“œκ°€ μ—†λ‹€!
  ...// λ‚˜λ¨Έμ§€ μ½”λ“œ μƒλž΅

}
  • 이클래슀λ₯Ό HashMap κ³Ό ν•¨κ»˜ μ‚¬μš©ν•œλ‹€κ³  ν•˜λ©΄
Map<PhoneNumber, String> m = new HashMap<PhoneNumber,String>();
m.put(new PhoneNumber(707,867, 5309),"jenny");
  • m.get(new PhoneNumber(707,867, 5309)) λ₯Ό ν˜ΈμΆœν•˜λ©΄ "jenny"κ°€ λ°˜ν™˜λ κ²ƒκ°™μ§€λ§Œ μ‹€μ œλ‘œλŠ” null

  • λ‘κ°œμ˜ μΈμŠ€ν„΄μŠ€κ°€ κ°œμž…λ˜μ–΄μžˆμŒμ„ μ£Όλͺ© ν•˜λ‚˜λŠ” HashMap 에 μ‚½μž…ν• λ•Œ ν‚€λ‘œ μ‚¬μš©λ˜μ—ˆκ³  μ²«λ²ˆμ§Έμ™€ λ™μΌν•œ 값을 κ°–λŠ” λ‘λ²ˆμ§Έ μΈμŠ€ν„΄μŠ€λŠ” 검색 킀에 μ‚¬μš©λ˜μ—ˆμŒ

  • PhoneNumber ν΄λž˜μŠ€μ—μ„œ hashCode λ©”μ†Œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œ ν•˜μ§€μ•Šμ•„μ„œ 두 μΈμŠ€ν„΄μŠ€κ°€ μ„œλ‘œλ‹€λ₯Έ ν•΄μ‹œ μ½”λ“œ 값을 κ°–κ²Œ 된 κ²ƒμž„

  • λ”°λΌμ„œ get λ©”μ†Œλ“œμ—μ„œλŠ” put λ©”μ†Œλ“œμ— μ €μž₯ν•œ μΈμŠ€ν„΄μŠ€μ˜ 것과 λ‹€λ₯Έ ν•΄μ‹œ 버킷(bucket)μ—μ„œ PhoneNumber μΈμŠ€ν„΄μŠ€λ₯Ό μ°ΎλŠ”λ‹€.

  • 심지어 두 μΈμŠ€ν„΄μŠ€κ°€ 같은 버킷에 놓이더라도 get λ©”μ†Œλ“œλŠ” null 을 λ°˜ν™˜ν•˜λŠ” κ²ƒμž„

  • HashMap 은 각 ν•­λͺ©κ³Ό μ—°κ΄€λœ ν•΄μ‹œ μ½”λ“œλ₯Ό μ €μž₯ν•˜λŠ” μ΅œμ ν™” μ½”λ“œλ₯Ό κ°–κ³  μžˆμ–΄ ν•΄μ‹œμ½”λ“œκ°’μ΄ μΌμΉ˜ν•˜μ§€ μ•ŠμœΌλ©΄ 객체의 동일 μ—¬λΆ€λ₯Ό ν™•μΈν•˜μ§€ μ•ŠκΈ° 떄문이닀.

  • hoshCode λ©”μ†Œλ“œ μΆ”κ°€ν•˜λ©΄ 해결됨 ν•˜μ§€λ§Œ λ°‘μ—μ²˜λŸΌ μ ˆλŒ€ μ΄λ ‡κ²Œ μ‚¬μš©ν•˜λ©΄μ•ˆλ¨

@Override public int hashCode(){ return 42; }
  • λ™μΌν•œ 객체듀이 같은 ν•΄μ‹œγ…—λ“œλ₯Ό κ°–κ²Œ λ˜λ―€λ‘œ 이 λ©”μ†Œλ“œλŠ” μ λ²•ν•˜λ‹€.

  • ν•˜μ§€λ§Œ λͺ¨λ“  객체가 λ‹€ λ˜‘κ°™μ€ ν•΄μ‹œμ½”λ“œλ₯Ό κ°–κ²Œ λ˜λŠ” μ΅œμ•…μ˜ λ©”μ†Œλ“œ

  • λͺ¨λ“  객체듀은 같은 버킷에 μœ„μΉ˜ν•˜λ―€λ‘œ 이 객체듀은 μ €μž₯ν•˜λŠ” ν•΄μ‹œ μ»¬λ ‰μ…˜(HashMap, HashSet, Hashtable)μ—μ„œλŠ” 링크 리슀트(linked list)λ₯Ό λ‹€μ‹œ μƒμ„±ν•œλ‹€.

    • κ²°κ΅­ ν”„λ‘œκ·Έλž¨μ€ κ°μ²΄μˆ˜μ— μ„ ν˜•μ μœΌλ‘œ λΉ„λ‘€ν•˜μ—¬ 느리게 싀행될 수 밖에 μ—†μŒ ν°ν•΄μ‹œ μ»¬λ ‰μ…˜λ“€μ˜ 경우 λ™μž‘μ΄ λ˜λŠ”μ§€ μ˜μ‹¬λ μ •λ„λ‘œ μ‹¬κ°ν•œ μ„±λŠ₯차이
  • 쒋은 ν•΄μ‹œ λ©”μ†Œλ“œλŠ” λ™μΌν•˜μ§€ μ•Šμ€ 객체듀에 λŒ€ν•΄ μ„œλ‘œ λ‹€λ₯Έ ν•΄μ‹œ μ½”λ“œλ₯Ό 만

  • ν•΄μ‰¬μ½”λ“œ 겨의 κ· μΌν•˜κ²Œ λΆ„ν¬ν•˜λŠ” 방법

  1. 예λ₯Ό λ“€μ–΄ 17κ³Ό 같이 0이 μ•„λ‹Œ μ–΄λ–€ μƒμˆ˜ 값을 result λΌλŠ” int λ³€μˆ˜μ— μ €μž₯ν•œλ‹€.
  2. 우리 객체의 각 μ£Όμš”ν•„λ“œ(equals λ©”μ†Œλ“œμ—μ„œ λΉ„κ΅ν•˜λŠ”)f에 λŒ€ν•΄ λ‹€μŒμ„ μˆ˜ν–‰ν•œλ‹€.

a. 각 ν•„λ“œμ— λŒ€ν•œ int νƒ€μž…μ˜ ν•΄μ‹œ μ½”λ“œ cλ₯Ό λ‹€μŒκ³Όκ°™μ΄ μ‚°μΆœν•œλ‹€.

  1. ν•„λ“œ fκ°€ boolean νƒ€μž…μ΄λ©΄ (f ? 1 : 0)
  2. ν•„λ“œ fκ°€ byte, char, short, int νƒ€μž…μ΄λ©΄, (int) f
  3. ν•„λ“œ fκ°€ long νƒ€μž…μ΄λ©΄ (int) (f ^(f >>>32))
  4. ν•„λ“œ fκ°€ float νƒ€μž…μ΄λ©΄ Float.floatToIntBits(f).
  5. ν•„λ“œ fκ°€ double νƒ€μž…μ΄λ©΄, Double.doubleToLongBits(f)λ₯Ό μ‹€ν–‰ν•œν›„ λ°˜ν•œλœ long νƒ€μž…μ˜ 값을 μ•žμ˜ 2.a.3 처럼 μ²˜λ¦¬ν•΄μ„œ ν•΄μ‹œμ½”λ“œ 값을 κ΅¬ν•œλ‹€
  6. ν•„λ“œ fκ°€ 객체 참쑰일 κ²½μš°λŠ” ν˜„μž¬(equals λ©”μ†Œλ“œκ°€ 호좜된)객체의 equals λ©”μ†Œλ“œμ—μ„œ κ·Έ ν•„λ“œλ₯Ό λΉ„κ΅ν•˜κΈ° μœ„ν•΄ fκ°€ μ°Έμ‘°ν•˜λŠ” 객체의 equals λ©”μ†Œλ“œλ₯Ό μž¬κ·€μ μœΌλ‘œ ν˜ΈμΆœν•œλ‹€.
    • 그러면 κ·Έ 객체의 ν•„λ“œμ— λŒ€ν•΄ hashCode λ©”μ†Œλ“œλ„ μž¬κ·€μ μœΌλ‘œ ν˜ΈμΆœλœλ‹€.
    • 만일 더 λ³΅μž‘ν•œ 비ꡐ가 ν•„μš”ν•˜λ‹€λ©΄ ν•„λ“œμ˜ ν‘œμ€€ ν˜•μ‹μ„ λ§Œλ“€μ–΄ μ²˜λ¦¬ν•˜κ³  κ·Έ ν‘œμ€€ν˜•μ‹μ— λŒ€ν•΄ hashCode λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•œλ‹€.
    • 만일 ν•„λ“œ f의 값이 null 이면 0을 λ°˜ν™˜ν•œλ‹€.(λ˜λ‹€λ₯Έ μ–΄λ–€ μƒμˆ˜ 값도 κ°€λŠ₯ν•˜μ§€λ§Œ κ΄€λ‘€μ μœΌλ‘œ 0을 μ‚¬μš©ν•œλ‹€.)
  7. ν•„λ“œ fκ°€ 배열이라면 λ°°μ—΄μ˜ 각 μš”μ†Œμ˜ λ³„κ°œμ˜ ν•„λ“œμ²˜λŸΌ μ²˜λ¦¬ν•œλ‹€. 즉 μœ„μ˜ κ·œμΉ™λ“€μ„ μ μš©ν•˜μ—¬ μ²˜λ¦¬ν•΄μ•Όν•  μš”μ†Œ 각각의 ν•΄μ‹œμ½”λ“œ 값을 μ‚°μΆœ
    • λ°”λ‘œ 2.b μˆ˜μ‹μ„ μ΄μš©ν•˜μ—¬ κ·Έκ°’λ“€μ˜ 합을 κ΅¬ν•œλ‹€
    • 만일 λ°°μ—΄ ν•„λ“œμ˜ λͺ¨λ“  μš”μ†Œλ₯Ό μ²˜λ¦¬ν•΄μ•Όλœλ‹€λ©΄ μžλ°” 1.5 버전에 μΆ”κ°€λ˜μ–΄ μ˜€λ²„λ‘œλ”©λœ Arrays.hashCode λ©”μ†Œλ“œλ“€ 쀑 ν•˜λ‚˜λ₯Ό μ‚¬μš©ν•  μˆ˜μžˆλ‹€.

b. μ•žμ˜ 2.a λ‹¨κ³„μ—μ„œ κ΅¬ν•œ ν•΄μ‹œμ½”λ“œ cλ₯Ό result에 ν•©κ³„ν•œλ‹€. - result = 31 * result + c; 3. resultλ₯Ό λ°˜ν™˜ν•œλ‹€. 4. hashCode λ©”μ†Œλ“œ μž‘μ„±μ΄ λλ‚˜λ©΄ λ™μΌν•œ μΈμŠ€ν„΄μŠ€λ“€μ΄ 같은 κ°’μ˜ ν•΄μ‹œμ½”λ“œλ₯Ό κ°–λŠ”μ§€ κ²€ν† ν•˜μž - λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜μ—¬ 잘 μ‹€ν–‰ λ˜λŠ”μ§€ 검증, 만일 λ™μΌν•œ μΈμŠ€ν„΄μŠ€λ“€μ΄ μ„œλ‘œ λ‹€λ₯Έ ν•΄μ‹œμ½”λ“œ 값을 κ°–λŠ”λ‹€λ©΄ κ·Έ 이유λ₯Ό μ°Ύμ•„ 문제λ₯Ό ν•΄κ²°

@Override public int hashCode(){
    int result=17;
    result = 31 * result + areaCode;
    result = 31 * result + prefix;
    result = 31 * result + lineNumber;
    return result;
}
  • 이 λ©”μ†Œλ“œμ—μ„œ PhoneNumber μΈμŠ€ν„΄μŠ€ μ„Έκ°€μ§€ μ€‘μš” ν•„λ“œλ§ŒμœΌλ‘œ κ°„λ‹¨νžˆ μ—°μ‚°ν•œ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜λ―€λ‘œ λ™μΌν•œ PhoneNumber μΈμŠ€ν„΄μŠ€λ“€μ€ λ˜‘κ°™μ€ ν•΄μ‹œ μ½”λ“œ 값을 κ°–λŠ”λ‹€.

  • PhoneNumber 에 맞게 μ™„λ²½ν•˜κ²Œ κ΅¬ν˜„λœ hashCode둜써 μžλ°” ν”Œλž«νΌ 라이브러리의 것과 동등

  • 이 λ©”μ†Œλ“œλŠ” κ°„λ‹¨ν•˜κ³  싀행속도가 λΉ λ₯΄λ‹€. 그리고 λ™μΌν•˜μ§€ μ•Šμ€ phoneNumber μΈμŠ€ν„΄μŠ€λ“€μ„ μ„œλ‘œ λ‹€λ₯Έ ν•΄μ‹œ 버킷에 잘 λΆ„μ‚°μ‹œν‚¨λ‹€.

  • λΆˆλ³€μ΄λ©΄μ„œ ν•΄μ‹œ μ½”λ“œ μ—°μ‚° λΉ„μš©μ΄ μ€‘μš”ν•œ 클래슀라면 ν•΄μ‹œ 값이 맀번 ν•„μš”ν• λ–„λ§ˆλ‹€ κ³„μ‚°ν•˜λŠ”κ²ƒ λ³΄λ‹€λŠ” 객체 내뢀에 ν•΄μ‹œμ½”λ“œλ₯Ό μ €μž₯ν•˜λŠ” 것을 κ³ λ €ν•΄λ³Όμˆ˜μžˆμŒ

  • 그런 νƒ€μž…μ˜ 객체듀을 ν•΄μ‹œν‚€λ‘œ μ‚¬μš©ν• κ±°λΌλ©΄ μΈμŠ€ν„΄μŠ€κ°€ μƒμ„±λ λ•Œ ν•΄μ‹œμ½”λ“œλ₯Ό μ‚°μΆœν•΄μ•Όν•¨

  • κ·Έλ ‡μ§€ μ•Šλ‹€λ©΄ hashCode λ©”μ†Œλ“œκ°€ 졜초 ν˜ΈμΆœλ λ•Œ λŠ¦μ€ μ΄ˆκΈ°ν™”(Lazy initialization) ν•  수있음

// Lazy initialization ν•˜λ©΄μ„œ 객체에 ν•΄μ‹œμ½”λ“œλ₯Ό μ €μž₯
private volatile int hashCode;

@Override
public int hashCode(){
 int result = hashCode;
 if( result == 0){
    result = 17;
    result = 31 * result + areaCode;
    result = 31 * result + prefix;
    result = 31 * result + lineNumber;
    hoshCode = result;
 }
 return result;
}
  • ν•΄μ‹œμ½”λ“œλ₯Ό μ‚°μΆœν• λ•ŒλŠ” μ„±λŠ₯ 항상을 이유둜 객체의 μ€‘μš” 뢀뢄을 μ œμ™Έμ‹œν‚€μ§€λ§μž.
    • 그둜 인해 ν•΄μ‹œλ©”μ†Œλ“œμ˜ μ‹€ν–‰μ†λ„λŠ” λ”λΉ¨λΌμ§ˆμˆ˜ μžˆμ§€λ§Œ ν’ˆμ§ˆ μ €ν•˜λ‘œ 인해 λ„ˆλ¬΄ 느렀 μ‚¬μš© λΆˆκ°€λŠ₯ν•œ μ •λ„κΉŒμ§€ ν•΄μ‹œ ν…Œμ΄λΈ”λ“€μ˜ μ„±λŠ₯을 λ–¨μ–΄ 트릴수있음

μ°Έκ³ 

  • μ±… μ΄νŽ™ν‹°λΈŒμžλ°”