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 ๊ท์ฝ์ ๋ณด๋ฉด ์ด์ง ์ ๋งค๋ชจํธํ๋ค.