2023. 2. 1. 15:09ใJAVA/Effective JAVA
item13. clone ์ฌ์ ์๋ ์ฃผ์ํด์ ์งํํ๋ผ.
Clone() ์ด๋?
: Object ํด๋์ค์ clone() ๋ฉ์๋๋ ์์ ์ ๋ณต์ ํ์ฌ ์๋ก์ด ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ์ผ์ ํ๋ค.
๋จ์ํ ์ธ์คํด์ค ๋ณ์์ ๊ฐ๋ง ๋ณต์ฌํ๊ธฐ ๋๋ฌธ์ ์ฐธ์กฐํ์ ์ ์ธ์คํด์ค ๋ณ์๊ฐ ์๋ ํด๋์ค๋ ์์ ํ ์ธ์คํด์ค ๋ณต์ฌ๊ฐ ์ด๋ฃจ์ด์ง์ง ์๋๋ค.
Clone()์ ์ฌ์ฉํ๋ ค๋ฉด ๋ณต์ ํ ํด๋์ค๊ฐ Cloneable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ผ ํ๋ค.

Cloneable ์ธํฐํ์ด์ค๋ ๋ค์ด๊ฐ๋ณด๋ฉด ์๋ฌด๋ฐ ๋ด์ฉ์ด ์๋ ๋น ์ธํฐํ์ด์ค์ธ๋ฐ ๊ทธ๋ฅ ์ด ์ธํฐํ์ด์ค๋ ํด๋น ํด๋์ค๊ฐ ๋ณต์  ๊ฐ๋ฅํ๋ค๋ ๊ฒ์ ์๋ ค์ค๋ค๊ณ ํ ์ ์๋ค.
public class PhoneNumber implements Cloneable{
    private final short areaCode, prefix, lineNum;
    public PhoneNumber(int areaCode, int prefix, int lineNum)
    {
        this.areaCode = rangeCheck(areaCode, 999, "area code");
        this.prefix = rangeCheck(prefix, 999, "prefix");
        this.lineNum = rangeCheck(lineNum, 9999, "line num");
        System.out.println("constructor is called !");
    }
    // toString์ด ๋ฐํ๋ ๊ฐ์ ํฌํจ๋ ์ ๋ณด๋ฅผ ์ป์ ์ ์๋ API๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ด ์ข๋ค.
    public short getAreaCode()
    {
        return areaCode;
    }
    public short getPrefix()
    {
        return prefix;
    }
    public short getLineNum()
    {
        return lineNum;
    }
    private static short rangeCheck(int val, int max, String arg)
    {
        if (val < 0 || val > max) {
            throw new IllegalArgumentException(arg + ": " + val);
        }
        return (short) val;
    }
    // PhoneNumber๋ฅผ ๋ฆฌํดํ๋๋ก ํด์ ํด๋ผ์ด์ธํธ๊ฐ ์ฌ์ฉํ  ๋ ํ์
 ์บ์คํ
์ ํ์ง ์์๋ ๋๋ค.
    @Override
    public PhoneNumber clone() {
        try {
            return (PhoneNumber) super.clone();
        } catch (CloneNotSupportedException e) { // ์ผ์ด๋  ์ ์๋ ์ผ์ด๋ค.
            throw new AssertionError(); // ๋์  ์๋ฌ๋ฅผ ๋์ ธ์คฌ์
        }
    }
    @Override
    public boolean equals(Object o)
    {
        if (this == o)
            return true;
        if (!(o instanceof PhoneNumber)) {
            return false;
        }
        PhoneNumber that = (PhoneNumber) o;
        return areaCode == that.areaCode && prefix == that.prefix && lineNum == that.lineNum;
    }
    @Override
    public int hashCode()
    {
        int result = Short.hashCode(areaCode);
        result = result + 31 * Short.hashCode(prefix);
        result =result +  31 * Short.hashCode(lineNum);
        return result;
    }
    @Override
    public String toString()
    {
        return String.format("%03d-%03d-%04d", areaCode, prefix, lineNum);
    }
    public static PhoneNumber of(String phoneNumberString)
    {
        String[] split = phoneNumberString.split("-");
        PhoneNumber phoneNumber = new PhoneNumber(Integer.parseInt(split[0]), Integer.parseInt(split[1]), Integer.parseInt(split[2]));
        return phoneNumber;
    }
}

Clone์ ์ฌ์ ์ํ๋๋ฐ IDE์ ๋์์ผ๋ก clone์ ์ฌ์ ์ํ๋ฉด ์ ๊ทผ์ ์ด์๊ฐ public ์ด ์๋ protected ์ฌ์ ๋ค๋ฅธํด๋์ค๊ฐ ๋ณต์ ํ ๋ clone๋ฉ์๋๋ฅผ ํธ์ถํ ์ ์์ผ๋ฏ๋ก public์ผ๋ก ๋ณ๊ฒฝํด์ค๋ค.

๊ทธ๋ฆฌ๊ณ ์ด๋ฅผ ์คํํด๋ณด๋ฉด

phoneNumber.clone()์ ํ๋ฉด PhoneNumber์ ์ ์์ ์ผ๋ก cloneํ ๊ฒ์ ํ์ธํ ์ ์๋ค.
์์ PhoneNumber๋ ๋ถ๋ณ ํด๋์ค๋ผ์ Cloneable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ , clone() ๋ฉ์๋๋ฅผ ์ฌ์ ์ํ๋ฉด ๋๋ค.
(Cloneable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ์ง ์์ผ๋ฉด clone์ ํ ์ ์๋ค.)
โ๏ธ ๊ทธ๋ฆฌ๊ณ ์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ผ๋ก ๋ฐ๋์ super.clone() ์ ์ฌ์ฉํด์ผ ํ๋ค๋ ์ ์ด๋ค.
public class Item implements Cloneable {
    private String name;
    // ์ด๋ ๊ฒ ๊ตฌํํ๋ฉด ํ์ ํด๋์ค์ clone()์ด ๊นจ์ง ์ ์๋ค.
    // ์์ฑ์๋ฅผ ํธ์ถํด์ ๋ง๋ค๋ฉด ์๋๋ค!!!!
    @Override
    public Item clone() {
        Item item = new Item();
        item.name = this.name;
        return item;
    }
  }
public class SubItem extends Item implements Cloneable{
    private String name;
    @Override
    public SubItem clone() {
        return (SubItem) super.clone(); // Item -> SubItem์ผ๋ก ๋ณํ์ ๋ชปํ๊ธฐ ๋๋ฌธ์ ์๋ฌ ๋ฐ์ํจ..!!
    }
    public static void main(String[] args) {
        SubItem subItem = new SubItem();
        SubItem clone = subItem.clone();
        System.out.println("(clone != null) = " + (clone != null));
        System.out.println("(clone.getClass() == subItem.getClass()) = " + (clone.getClass() == subItem.getClass()));
        System.out.println("clone.equals(subItem) = " + clone.equals(subItem));
    }
}
Item์์ Cloneable ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๊ณ clone()์ ์ฌ์ ์ํ ๋ , super.clone() ์ด ์๋ ์์ฑ์๋ฅผ ํธ์ถํ๋ฉด
Item์ ์์๋ฐ์ SubItem์์ cloneํ ๋, Item์ subItem์ผ๋ก ๋ณํํ ์ ์๊ธฐ ๋๋ฌธ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
Exception in thread "main" java.lang.ClassCastException: class chapter02.item13.clone_use_constructor.Item cannot be cast to class chapter02.item13.clone_use_constructor.SubItem (chapter02.item13.clone_use_constructor.Item and chapter02.item13.clone_use_constructor.SubItem are in unnamed module of loader 'app') at chapter02.item13.clone_use_constructor.SubItem.clone(SubItem.java:12) at chapter02.item13.clone_use_constructor.SubItem.main(SubItem.java:17)
// ์ ๋๋ก ๊ตฌํํ clone
@Override
public Item clone()  {
    Item result = null;
    try {
        result = (Item) super.clone();
        return result;
    } catch (CloneNotSupportedException e) {
        throw new AssertionError();
    }
}
Item clone์ super.clone์ผ๋ก ๋ฐ๊ฟ์ ๋ค์ ๊ตฌํํ ์ฝ๋์ธ๋ฐ ์ด๋ ๊ฒ ํ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์๋๋ค.

Clone ๊ท์ฝ
- x.clone() != x ๋ ๋ฐ๋์ true์ด๋ค.
- x.clone().getClass() == x.getClass() ๋ ๋ฐ๋์ true์ด๋ค.
- x.clone().equals(x) ๋ ture๊ฐ ์๋ ์๋ ์๋ค.
-> clone ๊ท์ฝ์ ๋ณด๋ฉด ์ด์ง ์ ๋งค๋ชจํธํ๋ค.