Copy Constructor & Default Constructor

บทนำ: ปัญหาของการ Copy Objects

บางครั้งต้องการ copy object เพื่อสร้าง object ใหม่ที่มี ข้อมูลเดียวกัน

java// ❌ ปัญหา: ไม่มีวิธีที่ดีในการ copy
public class Student {
    private String name;
    private double gpa;
    private int year;
    
    public Student(String name, double gpa, int year) {
        this.name = name;
        this.gpa = gpa;
        this.year = year;
    }
}

// ต้องการสร้าง copy
Student original = new Student("John", 3.75, 2);

// ❌ ต้องจำข้อมูล และ set ใหม่
Student copy = new Student(original.getName(), original.getGPA(), original.getYear());

// ❌ ยาว + ลืมได้
// ❌ ถ้า Student มี 10 attributes ต้อง set ทั้งหมด!

ปัญหา:

  • ❌ Copy ยุ่งเหยิง
  • ❌ ต้องรู้ attributes ทั้งหมด
  • ❌ ลืมได้

Copy Constructor คืออะไร?

Copy Constructor = Constructor ที่รับ object เดียวกัน และ copy ข้อมูล

textSyntax:
public ClassName(ClassName original) {
    this.field1 = original.field1;
    this.field2 = original.field2;
    // ...
}

ตัวอย่าง:
public Student(Student original) {
    this.name = original.name;
    this.gpa = original.gpa;
    this.year = original.year;
}

ตัวอย่างที่ 1: Copy Constructor พื้นฐาน

javapublic class Student {
    private String name;
    private double gpa;
    private int year;
    
    // Regular Constructor
    public Student(String name, double gpa, int year) {
        this.name = name;
        this.gpa = gpa;
        this.year = year;
    }
    
    // Copy Constructor
    public Student(Student original) {
        this.name = original.name;
        this.gpa = original.gpa;
        this.year = original.year;
    }
    
    public void displayInfo() {
        System.out.printf("Name: %s | GPA: %.2f | Year: %d\n", name, gpa, year);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        // สร้าง original
        Student original = new Student("John", 3.75, 2);
        
        // ✓ Copy ง่ายๆ
        Student copy = new Student(original);
        
        original.displayInfo();  // Name: John | GPA: 3.75 | Year: 2
        copy.displayInfo();      // Name: John | GPA: 3.75 | Year: 2
        
        // ✓ Copy มี ข้อมูลเดียวกัน แต่ต่างกัน object
        System.out.println("Same object? " + (original == copy));  // false
    }
}

Shallow Copy vs Deep Copy

ปัญหา: Shallow Copy

เมื่อ copy object ที่มี reference types (เช่น List, Array)

javaimport java.util.ArrayList;
import java.util.List;

public class Course {
    private String courseID;
    private List<String> students;
    
    // Regular Constructor
    public Course(String courseID) {
        this.courseID = courseID;
        this.students = new ArrayList<>();
    }
    
    // ❌ Shallow Copy (ผิด!)
    public Course(Course original) {
        this.courseID = original.courseID;
        this.students = original.students;  // ← reference ตัวเดียวกัน!
    }
    
    public void addStudent(String name) {
        students.add(name);
    }
    
    public void displayInfo() {
        System.out.printf("Course: %s | Students: %s\n", courseID, students);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        Course original = new Course("CS101");
        original.addStudent("John");
        
        // Shallow copy
        Course copy = new Course(original);
        
        // ❌ ปัญหา: เพิ่มที่ copy → original เปลี่ยนด้วย!
        copy.addStudent("Jane");
        
        original.displayInfo();  // Course: CS101 | Students: [John, Jane] ❌ เปลี่ยน!
        copy.displayInfo();      // Course: CS101 | Students: [John, Jane]
    }
}

ปัญหา: students เป็น reference type

  • original.students และ copy.students ชี้ไปยัง list เดียวกัน
  • เปลี่ยน copy.students → original.students เปลี่ยนด้วย

✓ Deep Copy (ถูกต้อง)

javaimport java.util.ArrayList;
import java.util.List;

public class Course {
    private String courseID;
    private List<String> students;
    
    // Regular Constructor
    public Course(String courseID) {
        this.courseID = courseID;
        this.students = new ArrayList<>();
    }
    
    // ✓ Deep Copy (ถูก!)
    public Course(Course original) {
        this.courseID = original.courseID;
        // ✓ สร้าง list ใหม่ แล้ว copy ข้อมูล
        this.students = new ArrayList<>(original.students);
    }
    
    public void addStudent(String name) {
        students.add(name);
    }
    
    public void displayInfo() {
        System.out.printf("Course: %s | Students: %s\n", courseID, students);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        Course original = new Course("CS101");
        original.addStudent("John");
        
        // Deep copy
        Course copy = new Course(original);
        
        // ✓ เพิ่มที่ copy → original ไม่เปลี่ยน!
        copy.addStudent("Jane");
        
        original.displayInfo();  // Course: CS101 | Students: [John] ✓ ไม่เปลี่ยน
        copy.displayInfo();      // Course: CS101 | Students: [John, Jane]
    }
}

แตกต่าง:

  • original.students = [John] (ไม่เปลี่ยน)
  • copy.students = [John, Jane] (เปลี่ยน)
  • แต่ละอันมี list ต่างกัน (independent)

ตัวอย่างที่ 2: Copy Constructor ด้วย Validation

javapublic class BankAccount {
    private String accountNumber;
    private String accountHolder;
    private double balance;
    
    // Regular Constructor
    public BankAccount(String accountNumber, String accountHolder, double balance) {
        if (accountNumber == null || accountNumber.isEmpty()) {
            throw new IllegalArgumentException("Account number required");
        }
        if (balance < 0) {
            throw new IllegalArgumentException("Balance cannot be negative");
        }
        
        this.accountNumber = accountNumber;
        this.accountHolder = accountHolder;
        this.balance = balance;
    }
    
    // Copy Constructor
    public BankAccount(BankAccount original) {
        if (original == null) {
            throw new IllegalArgumentException("Original account cannot be null");
        }
        
        // ✓ Copy ข้อมูล (ไม่ต้อง validate เพราะ original ผ่านแล้ว)
        this.accountNumber = original.accountNumber;
        this.accountHolder = original.accountHolder;
        this.balance = original.balance;
    }
    
    public void withdraw(double amount) {
        if (amount > this.balance) {
            throw new IllegalArgumentException("Insufficient balance");
        }
        this.balance -= amount;
    }
    
    public void displayInfo() {
        System.out.printf("Account: %s | Holder: %s | Balance: %.2f\n",
                         accountNumber, accountHolder, balance);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        BankAccount original = new BankAccount("ACC001", "John Doe", 50000);
        
        // ✓ Copy เสร็จเลย
        BankAccount copy = new BankAccount(original);
        
        System.out.println("=== Original ===");
        original.displayInfo();
        
        System.out.println("\n=== Copy ===");
        copy.displayInfo();
        
        // ✓ ใช้งาน copy ได้เลย
        System.out.println("\n=== Copy withdraws 10000 ===");
        copy.withdraw(10000);
        copy.displayInfo();
        
        System.out.println("Original unchanged:");
        original.displayInfo();
    }
}

Output:

text=== Original ===
Account: ACC001 | Holder: John Doe | Balance: 50000.00

=== Copy ===
Account: ACC001 | Holder: John Doe | Balance: 50000.00

=== Copy withdraws 10000 ===
Account: ACC001 | Holder: John Doe | Balance: 40000.00
Original unchanged:
Account: ACC001 | Holder: John Doe | Balance: 50000.00

Default Constructor คืออะไร?

Default Constructor = Constructor ที่ไม่มี parameter และ initialize ด้วย default values

textSyntax:
public ClassName() {
    // initialize ด้วย default values
}

ตัวอย่างที่ 3: Default Constructor

javapublic class Student {
    private String name;
    private double gpa;
    private int year;
    
    // Default Constructor
    public Student() {
        this.name = "Unknown";
        this.gpa = 0.0;
        this.year = 1;
    }
    
    // Regular Constructor
    public Student(String name, double gpa, int year) {
        this.name = name;
        this.gpa = gpa;
        this.year = year;
    }
    
    // Copy Constructor
    public Student(Student original) {
        this.name = original.name;
        this.gpa = original.gpa;
        this.year = original.year;
    }
    
    public void displayInfo() {
        System.out.printf("Name: %s | GPA: %.2f | Year: %d\n", name, gpa, year);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        // ✓ Default Constructor - สร้างแบบ placeholder
        Student s1 = new Student();
        s1.displayInfo();  // Name: Unknown | GPA: 0.00 | Year: 1
        
        // ✓ Regular Constructor
        Student s2 = new Student("John", 3.75, 2);
        s2.displayInfo();  // Name: John | GPA: 3.75 | Year: 2
        
        // ✓ Copy Constructor
        Student s3 = new Student(s2);
        s3.displayInfo();  // Name: John | GPA: 3.75 | Year: 2
    }
}

Java’s Implicit Default Constructor

❓ ถ้าไม่เขียน Constructor เลย?

java// ❌ ไม่เขียน Constructor
public class Student {
    private String name;
    private double gpa;
}

// Java สร้าง implicit default constructor อัตโนมัติ
// เหมือน:
public class Student {
    public Student() {
        // (empty - attributes มี default values)
    }
}

// สามารถ new ได้
Student student = new Student();
// name = null (default for String)
// gpa = 0.0 (default for double)

⚠️ ถ้าเขียน Constructor ใด Constructor หนึ่ง

java// ⚠️ ถ้าเขียน Constructor ที่มี parameter
public class Student {
    private String name;
    private double gpa;
    
    // Regular Constructor
    public Student(String name, double gpa) {
        this.name = name;
        this.gpa = gpa;
    }
}

// Java จะ NOT สร้าง implicit default constructor!
// ❌ ต้องเขียน default constructor เอง

Student s1 = new Student("John", 3.75);  // ✓ OK
Student s2 = new Student();              // ❌ ERROR! No default constructor

✓ ถ้าต้องใช้ทั้ง Default + Regular

java// ✓ เขียน default constructor เอง
public class Student {
    private String name;
    private double gpa;
    
    // Default Constructor
    public Student() {
        this.name = "Unknown";
        this.gpa = 0.0;
    }
    
    // Regular Constructor
    public Student(String name, double gpa) {
        this.name = name;
        this.gpa = gpa;
    }
}

// ✓ ทั้งคู่ใช้ได้
Student s1 = new Student();              // ✓ OK
Student s2 = new Student("John", 3.75);  // ✓ OK

ตัวอย่างที่ 4: Multiple Constructors Together

javapublic class Product {
    private String productID;
    private String productName;
    private double price;
    private int quantity;
    
    // Constructor 1: Default Constructor
    public Product() {
        this("", "", 0, 0);  // ← เรียก Constructor 3 (master)
    }
    
    // Constructor 2: Basic info
    public Product(String id, String name) {
        this(id, name, 0, 0);  // ← เรียก Constructor 3
    }
    
    // Constructor 3: All info (Master)
    public Product(String id, String name, double price, int qty) {
        if (id == null || id.isEmpty()) {
            throw new IllegalArgumentException("Product ID required");
        }
        this.productID = id;
        this.productName = name;
        this.price = price;
        this.quantity = qty;
    }
    
    // Constructor 4: Copy Constructor
    public Product(Product original) {
        this(original.productID, original.productName, 
             original.price, original.quantity);
    }
    
    public void displayInfo() {
        System.out.printf("ID: %s | Name: %s | Price: %.2f | Qty: %d\n",
                         productID, productName, price, quantity);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        System.out.println("=== Default Constructor ===");
        Product p1 = new Product();
        p1.displayInfo();  // ID:  | Name:  | Price: 0.00 | Qty: 0
        
        System.out.println("\n=== Basic Info Constructor ===");
        Product p2 = new Product("P001", "Laptop");
        p2.displayInfo();  // ID: P001 | Name: Laptop | Price: 0.00 | Qty: 0
        
        System.out.println("\n=== Full Info Constructor ===");
        Product p3 = new Product("P002", "Mouse", 500, 50);
        p3.displayInfo();  // ID: P002 | Name: Mouse | Price: 500.00 | Qty: 50
        
        System.out.println("\n=== Copy Constructor ===");
        Product p4 = new Product(p3);
        p4.displayInfo();  // ID: P002 | Name: Mouse | Price: 500.00 | Qty: 50
    }
}

Output:

text=== Default Constructor ===
ID:  | Name:  | Price: 0.00 | Qty: 0

=== Basic Info Constructor ===
ID: P001 | Name: Laptop | Price: 0.00 | Qty: 0

=== Full Info Constructor ===
ID: P002 | Name: Mouse | Price: 500.00 | Qty: 50

=== Copy Constructor ===
ID: P002 | Name: Mouse | Price: 500.00 | Qty: 50

ตัวอย่างที่ 5: Copy Constructor ด้วย Collections

javaimport java.util.ArrayList;
import java.util.List;

public class Student {
    private String studentID;
    private String name;
    private List<String> enrolledCourses;
    
    // Default Constructor
    public Student() {
        this("", "", new ArrayList<>());
    }
    
    // Regular Constructor
    public Student(String id, String name) {
        this(id, name, new ArrayList<>());
    }
    
    // Master Constructor
    public Student(String id, String name, List<String> courses) {
        this.studentID = id;
        this.name = name;
        // ✓ Deep copy: สร้าง list ใหม่
        this.enrolledCourses = new ArrayList<>(courses);
    }
    
    // Copy Constructor
    public Student(Student original) {
        this(original.studentID, original.name, original.enrolledCourses);
        // ✓ Master constructor ทำ deep copy ให้
    }
    
    public void enrollCourse(String course) {
        enrolledCourses.add(course);
        System.out.println("✓ Enrolled: " + course);
    }
    
    public void displayInfo() {
        System.out.printf("ID: %s | Name: %s | Courses: %s\n",
                         studentID, name, enrolledCourses);
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        // สร้าง original
        Student original = new Student("S001", "John");
        original.enrollCourse("CS101");
        original.enrollCourse("MATH101");
        
        System.out.println("=== Original ===");
        original.displayInfo();
        
        // Copy
        System.out.println("\n=== Copy ===");
        Student copy = new Student(original);
        copy.displayInfo();
        
        // ✓ เพิ่ม course ที่ copy
        System.out.println("\n=== Copy enrolls more courses ===");
        copy.enrollCourse("ENG101");
        copy.displayInfo();
        
        // ✓ Original ไม่เปลี่ยน
        System.out.println("\n=== Original unchanged ===");
        original.displayInfo();
    }
}

Output:

text✓ Enrolled: CS101
✓ Enrolled: MATH101
=== Original ===
ID: S001 | Name: John | Courses: [CS101, MATH101]

=== Copy ===
ID: S001 | Name: John | Courses: [CS101, MATH101]

=== Copy enrolls more courses ===
✓ Enrolled: ENG101
ID: S001 | Name: John | Courses: [CS101, MATH101, ENG101]

=== Original unchanged ===
ID: S001 | Name: John | Courses: [CS101, MATH101]

Best Practices: Copy Constructor

1. ใช้ Deep Copy สำหรับ Reference Types

java// ✓ ดี: Deep copy
public class Order {
    private List<Item> items;
    
    public Order(Order original) {
        // ✓ สร้าง list ใหม่
        this.items = new ArrayList<>(original.items);
    }
}

// ❌ ไม่ดี: Shallow copy
public class Order {
    private List<Item> items;
    
    public Order(Order original) {
        // ❌ ใช้ reference เดียวกัน
        this.items = original.items;
    }
}

2. Null Check

java// ✓ ดี: Check null
public class Account {
    private String accountNumber;
    
    public Account(Account original) {
        if (original == null) {
            throw new IllegalArgumentException("Original cannot be null");
        }
        this.accountNumber = original.accountNumber;
    }
}

// ⚠️ ไม่ดี: ไม่ check
public class Account {
    public Account(Account original) {
        this.accountNumber = original.accountNumber;  // NPE ถ้า original = null
    }
}

3. Default Constructor ใช้ Chaining

java// ✓ ดี: Default chaining
public class Employee {
    public Employee() {
        this("", "", 0);  // ← เรียก master
    }
    
    public Employee(String id, String name, double salary) {
        // master initialization
    }
}

// ❌ ไม่ดี: Duplicate code
public class Employee {
    public Employee() {
        this.id = "";
        this.name = "";
        this.salary = 0;
    }
    
    public Employee(String id, String name, double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }
}

ตัวอย่างสุดท้าย: Complete Class ด้วยทุก Constructors

javaimport java.util.ArrayList;
import java.util.List;

public class Course {
    private String courseID;
    private String courseName;
    private int credits;
    private List<String> enrolledStudents;
    
    // Constructor 1: Default
    public Course() {
        this("", "", 0);
    }
    
    // Constructor 2: Basic info
    public Course(String courseID, String courseName) {
        this(courseID, courseName, 3);
    }
    
    // Constructor 3: Master
    public Course(String courseID, String courseName, int credits) {
        if (courseID == null || courseID.isEmpty()) {
            throw new IllegalArgumentException("Course ID required");
        }
        if (credits < 1 || credits > 4) {
            throw new IllegalArgumentException("Credits must be 1-4");
        }
        
        this.courseID = courseID;
        this.courseName = courseName;
        this.credits = credits;
        this.enrolledStudents = new ArrayList<>();
    }
    
    // Constructor 4: Copy Constructor
    public Course(Course original) {
        if (original == null) {
            throw new IllegalArgumentException("Original cannot be null");
        }
        
        this.courseID = original.courseID;
        this.courseName = original.courseName;
        this.credits = original.credits;
        // ✓ Deep copy
        this.enrolledStudents = new ArrayList<>(original.enrolledStudents);
        
        System.out.println("✓ Course copied: " + courseName);
    }
    
    public void enrollStudent(String studentName) {
        enrolledStudents.add(studentName);
    }
    
    public int getEnrollmentCount() {
        return enrolledStudents.size();
    }
    
    public void displayInfo() {
        System.out.printf("ID: %s | Name: %s | Credits: %d | Students: %d\n",
                         courseID, courseName, credits, enrolledStudents.size());
    }
}

// ใช้งาน
public class Main {
    public static void main(String[] args) {
        System.out.println("=== 1. Default Constructor ===");
        Course c1 = new Course();
        c1.displayInfo();
        
        System.out.println("\n=== 2. Basic Info Constructor ===");
        Course c2 = new Course("CS101", "OOP");
        c2.displayInfo();
        
        System.out.println("\n=== 3. Full Info Constructor ===");
        Course c3 = new Course("CS102", "Data Structures", 4);
        c3.enrollStudent("John");
        c3.enrollStudent("Jane");
        c3.enrollStudent("Bob");
        c3.displayInfo();
        
        System.out.println("\n=== 4. Copy Constructor ===");
        Course c4 = new Course(c3);
        c4.displayInfo();
        
        System.out.println("\n=== Add student to copy ===");
        c4.enrollStudent("Alice");
        c4.displayInfo();
        
        System.out.println("\n=== Original unchanged ===");
        c3.displayInfo();
    }
}

Output:

text=== 1. Default Constructor ===
ID:  | Name:  | Credits: 0 | Students: 0

=== 2. Basic Info Constructor ===
ID: CS101 | Name: OOP | Credits: 3 | Students: 0

=== 3. Full Info Constructor ===
ID: CS102 | Name: Data Structures | Credits: 4 | Students: 3

=== 4. Copy Constructor ===
✓ Course copied: Data Structures
ID: CS102 | Name: Data Structures | Credits: 4 | Students: 3

=== Add student to copy ===
ID: CS102 | Name: Data Structures | Credits: 4 | Students: 4

=== Original unchanged ===
ID: CS102 | Name: Data Structures | Credits: 4 | Students: 3

สรุป: Copy Constructor & Default Constructor

text┌───────────────────────────────────────────────────┐
│  Copy Constructor & Default Constructor          │
├───────────────────────────────────────────────────┤
│                                                   │
│  DEFAULT CONSTRUCTOR                             │
│  ├─ ไม่มี parameter                              │
│  ├─ Initialize ด้วย default values              │
│  └─ ใช้กำหนดค่า placeholder/ค่าเริ่มต้น         │
│                                                   │
│  COPY CONSTRUCTOR                                │
│  ├─ รับ object เดียวกัน เป็น parameter          │
│  ├─ Copy ข้อมูลทั้งหมด                           │
│  ├─ ✓ Deep copy สำหรับ reference types          │
│  └─ สร้าง independent copy                       │
│                                                   │
│  SHALLOW vs DEEP COPY                            │
│  ├─ Shallow: reference type ชี้เดียวกัน        │
│  │           (ปัญหา: เปลี่ยนกระทบกัน)          │
│  ├─ Deep: reference type ต่างกัน               │
│  │         (ถูก: independent copies)            │
│  └─ ใช้ new ArrayList(original) เป็นต้น        │
│                                                   │
│  IMPLICIT vs EXPLICIT                            │
│  ├─ ถ้าไม่เขียน constructor                     │
│  │  Java สร้าง implicit default                 │
│  ├─ ถ้าเขียน constructor หนึ่งตัว              │
│  │  Java ไม่สร้าง implicit default             │
│  └─ ต้องเขียน default เอง                      │
│                                                   │
│  BEST PRACTICES                                  │
│  ├─ ใช้ chaining เพื่อ reuse code              │
│  ├─ Master constructor เหนือสุด                 │
│  ├─ Null check ใน copy constructor             │
│  └─ Deep copy สำหรับ collections               │
│                                                   │
└───────────────────────────────────────────────────┘