View Full Version : نا مفهوم بودن Copy Constructor
gsshop
سه شنبه 07 اردیبهشت 1395, 12:13 عصر
با سلام خدمت دوستان من مفهوم سازنده ها یا همون Constructor ها رو بلدم اما درک و مفهوم سازنده کپی یا همون Copy Constructor برام نا مفهومه و دقیقا متوجه نمیشم چه کاری انجام میده ممنون میشم اگه کسی از دوستان بتونه کار و مفهوم Copy Constructor رو برای من باز کنه و با یک مثال سینتکس رو به من نشون بده که متوجه بشم و سوال دیگه اینکه ایا Copy Constructor کاربردی هم داره هنوز ؟ اگر داره در چه جاهایی میشه ازش استفاده کرد
vahid-p
چهارشنبه 08 اردیبهشت 1395, 19:38 عصر
تقریبا همون کار متد clone() رو انجام میده (گرچه در این لینک (https://adtmag.com/articles/2000/01/18/effective-javaeffective-cloning.aspx) توضیح داده که چرا از 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!
پس یه سری نکات داره که باید رعایت بشه.
محمد فدوی
پنج شنبه 09 اردیبهشت 1395, 16:43 عصر
... (بخونید immutable چیه تا دلیلش رو متوجه بشید، خلاصش اینه که هر تغییری در مقادیر این آبجکت، یک آبجکت جدیدی رو میسازه و قبلی تغییر نمیکنه)
چون اسم از طراحی «تغییرناپذیر» (Immutable) به میون اومد مناسب دیدم که توضیحی در این مورد بدم.. درسته که با روشهای مختلفی میتونیم یک شیٔ رو همانندسازی (Clone) کنیم، اما در اکثر موارد راه هوشمندانهتر اینه که سعی کنیم طراحیمون رو Immutable نگهداریم؛ اینجوری با بسیاری از محدودیتها و پیچیدگیها روبرو نیستیم و در مقابل تضمین میکنیم مشکلی مثل چیزی که در مورد Assignها مثال زدی پیش نمیآد.
البته این معنیش این نیست که طراحی Immutable همیشه خوبه!
public class Example {
private final int x;
private final int y;
private final String z;
public Example(int x, int y, String z){
this.x = x;
this.y = y;
this.z = z;
}
public Example withX(int x) {
return new Example(x, this.y, this.z);
}
public Example withY(int y) {
return new Example(this.x, y, this.z);
}
public Example withZ(String z) {
return new Example(this.x, this.y, z);
}
@Override
public String toString() {
return "X=" + x + ", Y=" + y + ", Z=" + z;
}
}
اینجوری علاوه بر سادهشدن کار خودمون، احتمال اشتباه استفاده کردن از کلاسمون رو به حداقل میرسونیم:
public static void main(String[] args) {
Example o1 = new Example(2, 2, "Steve");
Example o2 = o1.withY(3).withZ("Jobs");
System.out.println(o1);
System.out.println(o2);
}
vBulletin® v4.2.5, Copyright ©2000-1404, Jelsoft Enterprises Ltd.