سازنده Copy در جاوا — از صفر تا صد
منظور از سازنده Copy در جاوا یک کلاس «سازنده» (Constructor) است که شیئی را با استفاده از شیء دیگری از همان کلاس جاوا میسازد. این سازنده در مواردی مفید است که بخواهیم یک شیئ پیچیده را که چندین فیلد دارد، کپی کنیم یا این که بخواهیم یک «کپی عمیق» (Deep Copy) از یک شیئ موجود ایجاد کنیم.
ایجاد یک سازنده Copy در جاوا
برای ایجاد یک سازنده Copy ابتدا باید یک سازنده اعلان کنیم که شیئی از همان نوع به صورت یک پارامتر میگیرد:
1public class Employee {
2 private int id;
3 private String name;
4
5 public Employee(Employee employee) {
6 }
7}
سپس هر فیلد از شیء ورودی را به وهله جدیدی کپی میکنیم:
1public class Employee {
2 private int id;
3 private String name;
4
5 public Employee(Employee employee) {
6 this.id = employee.id;
7 this.name = employee.name;
8 }
9}
تا به اینجا یک «کپی سطحی» (Shallow Copy) داریم که مناسب است، زیرا همه فیلدهای ما (در این مثال یک int و یک String) یا از نوع ابتدایی (Premitive) یا نوع «تغییرناپذیر» (Immutable) هستند. اگر کلاس جاوا دارای فیلدهای تغییرپذیر باشد، در این صورت میتوانیم به جای این کار یک کپی عمیق درون سازنده copy آن ایجاد کنیم. با بهرهگیری از یک کپی عمیق، شیء جدیداً ایجادشده، مستقل از شیء اصلی خواهد بود، زیرا یک کپی متمایز از هر شیء تغییرپذیر ایجاد میکنیم:
1public class Employee {
2 private int id;
3 private String name;
4 private Date startDate;
5
6 public Employee(Employee employee) {
7 this.id = employee.id;
8 this.name = employee.name;
9 this.startDate = new Date(employee.startDate.getTime());
10 }
11}
سازنده Copy یا Clone
در جاوا میتوانیم از متد Clone برای ایجاد یک شیء از روی شیء موجود استفاده کنیم. با این حال سازنده Copy نسبت به متد Clone برخی مزیتها به شرح زیر دارد:
- پیادهسازی سازنده Copy بسیار سادهتر است. به این ترتیب نیازی به پیادهسازی اینترفیس Cloneable و مدیریت استثنای CloneNotSupportedException نداریم.
- متد Clone یک ارجاع Object عمومی بازگشت میدهد. از این رو باید آن را به نوع مناسب «تبدیل» (Typecast) کنیم.
- در متد Clone امکان انتساب یک مقدار به فیلد Final وجود ندارد، اما این کار در سازنده Copy امکانپذیر است.
مشکلات وراثتی
سازندههای Copy در جاوا به زیرکلاسها به ارث نمیرسند. از این رو اگر تلاش کنید یک شیء فرزند از یک ارجاع به کلاس والد مقداردهی کنید، با مشکل casting در زمان کلون کردن آن با سازنده Copy مواجه میشوید. برای نشان دادن این مشکل، در مثال زیر ابتدا یک زیرکلاس از Employee و از سازنده Copy آن ایجاد میکنیم:
1public class Manager extends Employee {
2 private List<Employee> directReports;
3 // ... other constructors
4
5 public Manager(Manager manager) {
6 super(manager.id, manager.name, manager.startDate);
7 this.directReports = directReports.stream()
8 .collect(Collectors.toList());
9 }
10}
سپس یک متغیر به نام Employee اعلان میکنیم و با سازنده Manager آن را مقداردهی میکنیم:
1Employee source = new Manager(1, "Baeldung Manager", startDate, directReports);
از آنجا که نوع ارجاع از نوع Employee است، باید آن را به نوع Manager تبدیل کنیم تا بتوانیم از سازنده Copy کلاس Manager بهره بگیریم:
1Employee clone = new Manager((Manager) source);
بدین ترتیب ممکن است در زمان اجرا با استثنای ClassCastException مواجه شویم، زیرا شیء ورودی یک وهله از کلاس Manager نیست. یک روش برای اجتناب از این تبدیل نوع در سازنده copy آن است که یک متد ارثبری جدید برای هر دو کلاس ایجاد کنیم:
1public class Employee {
2 public Employee copy() {
3 return new Employee(this);
4 }
5}
6
7public class Manager extends Employee {
8 @Override
9 public Employee copy() {
10 return new Manager(this);
11 }
12}
در هر متد کلاس، سازنده Copy آن را با ورودی این شیء فراخوانی میکنیم. به این ترتیب میتوانیم مطمئن باشیم که شیء تولیدشده برابر با شیء فراخواننده است:
1Employee clone = source.copy();
سخن پایانی
در این راهنما با شیوه ایجاد سازنده Copy با معرفی برخی مثالها آشنا شدیم. ضمناً به بررسی دلایل اجتناب از متد Clone پرداختیم. سازنده Copy یک مشکل تبدیل نوع دارد که در زمان استفاده از آن برای کلون کردن شیء کلاس فرزند که نوع ارجاعی آن از نوع کلاس والد است بروز مییابد. برای این مشکل نیز راهحلی ارائه کردیم.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای جاوا (Java)
- مجموعه آموزشهای برنامهنویسی
- گنجینه آموزشهای جاوا (Java)
- متدهای جاوا — به زبان ساده
- آموزش مقدماتی جاوا (بخش اول) — از صفر تا صد
==