تقریبا همون کار متد clone() رو انجام میده (گرچه در این لینک توضیح داده که چرا از clone استفاده نکنید و حتی در برخی داکیومنت های لایبرری اندروید هم در خصوص این متد چنین توضیحاتی داده بود.
اما به جای clone میتونیم از copy constructor استفاده کنیم یا متدی که چنین کاری رو انجام بده.
خب برای چی اصلا به چنین چیزی نیاز داریم. فرض کنید ما کلاسی داریم به این شکل:
public class Example {
private int x;
private int y;
private String z;
public Example(){
this.x=10;
this.y=10;
this.z="Hi";
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setZ(String z) {
this.z = z;
}
}
فرض کنید بخوایم یه آبجکت ازش بسازیم و پارامترهاش رو تغییر بدیم.
public static void main(String[] args) {
Example o1=new Example();
o1.setX(10);
o1.setY(20);
o1.setZ("Ali");
System.out.println(o1.toString());
}
}
خروجی:
X=10, Y=20, Z=Ali
اما فرض کنید میخوایم آبجکتی رو بسازیم که دقیقا همون مقادیر این آبجکت رو داشته باشه و فقط مقدار z رو تغییر بدیم و به اسم hasan تغییر بدیم. خب. مطمئنا میگید:
public static void main(String[] args) {
Example o1=new Example();
o1.setX(10);
o1.setY(20);
o1.setZ("Ali");
Example o2=o1;
o2.setZ("Hasan");
System.out.println(o1.toString());
System.out.println(o2.toString());
}
اما خروجیمون میشه:
X=10, Y=20, Z=Hasan
X=10, Y=20, Z=Hasan
در صورتی که انتظار داشتیم اولی Ali باقی بمونه.
این دقیقا به خاطر اینه که Assign کردن یک آبجکت به آبجکت (o2=o1) دیگه، فقط یک اشاره گر به همون آبجکت اولی (o1) هست و در نتیجه هر تغییری بر روی دومی (o2) همون آبجکت اولی تغییر خواهد کرد و در اصل فقط همون یک آبجکت وجود داره.
حتی اگر در مثال بالا به این صورت عمل کنید هم تفاوتی نداره:
Example o2=new Example();
o2=o1;
و نتیجه همون خواهد بود چرا که آبجکت جدید بلااستفاده میشه. برای اینکار شما باید تمام فیلدهای primitive یک آبجکت رو کپی کنید (مانند int, double, float ,... چون متغیرهایی هستند که مقدارشون ذخیره میشه و نه فقط یک رفرنس به محل شی). اگر چه String یک آبجکت است و primitive نیست، اما چون immutable است با اینکه نمیشه کپی کرد و اما همون Assign کردن جواب میده (بخونید immutable چیه تا دلیلش رو متوجه بشید، خلاصش اینه که هر تغییری در مقادیر این آبجکت، یک آبجکت جدیدی رو میسازه و قبلی تغییر نمیکنه)
خب پس برای اینکار یک کانستراکتور مینویسیم که از این به بعد بتونیم راحت یک آبجکت شبیه آبجکتی که داشتیم بسازیم. برای اینکار بهترین محل همون کلاس هست، چرا که به فیلدهای private اش دسترسی داریم و نیازی به متدهای get نیست ( برای کلاس های خارجی فقط میتونیم از متد get به فیلد های private دسترسی داشته باشیم که بدیهیه)
خب پس به کلاس اولیمون این رو اضافه می کنیم:
public Example(Example ex){
this.x=ex.x;
this.y=ex.y;
this.z=ex.z;
}
این نکته جالبه با اینکه ما به فیلدهای یک آبجکت ex از نوع private دسترسی داریم ولی کامپایلر هیچ ارروری نمیده، چون تشخیص میده که این کلاس و کلاس آبجکتی که بهش دسترسی داریم یکیه.
اما کد:
public static void main(String[] args) {
Example o1=new Example();
o1.setX(10);
o1.setY(20);
o1.setZ("Ali");
Example o2=new Example(o1);
o2.setZ("Hasan");
System.out.println(o1.toString());
System.out.println(o2.toString());
}
خروجی:
X=10, Y=20, Z=Ali
X=10, Y=20, Z=Hasan
دقیقا همونی که میخواستیم. خب واضحه!
اینم یه نمونه دیگه:
public Example(int x, int y, String z){
this.x=x;
this.y=y;
this.z=z;
}
public Example(Example ex){
this(ex.x,ex.y,ex.z);
}
البته باید توجه داشته باشی که برای تمام فیلدهای primitive و فیلد آبجکت ها immutable اینکار رو مستقیما میتونید انجام بدید (و باید برای تمام فیلدها باشه)
اما فرض کنید یکی از فیلدها خودش یک آبجکت بود. در اینحالت شما در کانستراکتور کپی خودتون باید کپی از اون آبجکت هم بسازید. البته توجه کنید در این حالت نمیتونید از خود این کلاس استفاده کنید چرا که باید کلاس ها به جایی برسن که دوباره نخواد کپی از یک آبجکت بسازه. یعنی آخرش باید به فیلدهای primitive برسن. فکر کنم یکم گنگ شد. بهتره یه مثال بزنم.
در چنین حالتی ما نمی تونیم از آبجکتی از کلاس فعلیمون داشته باشیم و از اون کپی بسازیم:
public class Example {
private int x;
private int y;
private String z;
private Example o;
public Example(int x, int y, String z){
this.x=x;
this.y=y;
this.z=z;
o=new Example(30,40,"Saeed");
}
public Example(Example ex){
this(ex.x,ex.y,ex.z);
this.o=new Example(ex.o);
}
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
public void setZ(String z) {
this.z = z;
}
@Override
public String toString() {
return "X="+x+", Y="+y+", Z="+z;
}
}
دلیلشم اینه تو یک loop تا بی نهایت آبجکت میسازه و در نهایت Stackoverflow!
پس یه سری نکات داره که باید رعایت بشه.