Chào mừng các bạn đến với bài học Java số 31, nơi chúng ta sẽ tìm hiểu về ép kiểu trong OOP. Trước đây, chúng ta đã học về ép kiểu ở bài số 6, nhưng lần này, chúng ta sẽ xem xét việc ép kiểu trên các dữ liệu không phải nguyên thủy.
Ép kiểu trong OOP cũng có hai dạng, tức là ép kiểu tường minh và ép kiểu ngầm định.
Ép Kiểu Ngầm Định Với Các Đối Tượng Cùng Một Lớp
Đầu tiên, hãy xem xét việc ép kiểu ngầm định giữa các đối tượng cùng một lớp. Khi bạn gán các đối tượng này cho nhau, kết quả là chúng sẽ chia sẻ cùng một nội dung. Ví dụ, nếu bạn gán đối tượng nhanVien2
cho nhanVien1
, kết quả là nhanVien1
và nhanVien2
sẽ có cùng giá trị.
Tuy nhiên, điều này chỉ áp dụng cho các đối tượng nguyên thủy. Khi bạn gán hai đối tượng không phải nguyên thủy, việc gán này được coi như một phép ép kiểu. Hãy xem ví dụ dưới đây:
public class NhanVien { protected String ten; // Các phương thức getter/setter public void xuatThongTin() { System.out.println("Nhân viên: " + ten); } } public class MainClass { public static void main(String[] args) { NhanVien nhanVien1 = new NhanVien(); NhanVien nhanVien2 = new NhanVien(); nhanVien1.setTen("Hùng"); nhanVien2.setTen("Trang"); // Kết quả: "Nhân viên: Hùng" nhanVien1.xuatThongTin(); // Kết quả: "Nhân viên: Trang" nhanVien2.xuatThongTin(); // Gán đối tượng nhanVien1 cho nhanVien2 nhanVien2 = nhanVien1; // Kết quả: "Nhân viên: Hùng" nhanVien1.xuatThongTin(); // Kết quả: "Nhân viên: Hùng" nhanVien2.xuatThongTin(); // Thay đổi giá trị thuộc tính của nhanVien2 nhanVien2.setTen("Khải"); // Kết quả: "Nhân viên: Khải" nhanVien1.xuatThongTin(); // Kết quả: "Nhân viên: Khải" nhanVien2.xuatThongTin(); } }
Như bạn có thể thấy trong ví dụ trên, khi gán nhanVien1
cho nhanVien2
, cả hai đối tượng sẽ cùng chia sẻ nội dung. Khi bạn thay đổi giá trị của nhanVien2
, nhanVien1
cũng sẽ bị thay đổi tương tự.
Ép Kiểu Ngầm Định Từ Lớp Con Sang Lớp Cha
Tương tự như ép kiểu ngầm định giữa các đối tượng cùng lớp, khi chuyển đổi từ lớp con sang lớp cha cũng sẽ xảy ra ép kiểu ngầm định. Điều này xảy ra khi có sự chuyển đổi dữ liệu từ kiểu dữ liệu có kích thước nhỏ sang kiểu dữ liệu có kích thước lớn hơn.
Hãy xem ví dụ sau:
public class NhanVienFullTime extends NhanVien { @Override public void xuatThongTin() { System.out.println("Nhân viên toàn thời gian: " + ten); } } public class MainClass { public static void main(String[] args) { NhanVien nhanVien = new NhanVien(); NhanVienFullTime nhanVienFullTime = new NhanVienFullTime(); nhanVien.setTen("Hùng"); nhanVienFullTime.setTen("Trang"); // Kết quả: "Nhân viên: Hùng" nhanVien.xuatThongTin(); // Kết quả: "Nhân viên toàn thời gian: Trang" nhanVienFullTime.xuatThongTin(); // Ép kiểu ngầm định từ NhanVienFullTime về NhanVien nhanVien = nhanVienFullTime; // Kết quả: "Nhân viên toàn thời gian: Trang" nhanVien.xuatThongTin(); // Kết quả: "Nhân viên toàn thời gian: Trang" nhanVienFullTime.xuatThongTin(); // Thay đổi giá trị thuộc tính của nhanVienFullTime nhanVienFullTime.setTen("Khải"); // Kết quả: "Nhân viên toàn thời gian: Khải" nhanVien.xuatThongTin(); // Kết quả: "Nhân viên toàn thời gian: Khải" nhanVienFullTime.xuatThongTin(); } }
Trong ví dụ trên, khi ép kiểu ngầm định từ NhanVienFullTime
về NhanVien
, chúng ta có thể sử dụng nhanVien
như một đối tượng NhanVien
, nhưng vẫn giữ tính đa hình của NhanVienFullTime
. Khi thay đổi giá trị của nhanVienFullTime
, nhanVien
cũng sẽ bị thay đổi tương tự.
Từ Khóa instanceof
Khi sử dụng ép kiểu tường minh, rất quan trọng phải kiểm tra đối tượng có thuộc lớp cụ thể nào không trước khi thực hiện ép kiểu. Để làm điều này, chúng ta có thể sử dụng từ khóa instanceof
.
Ví dụ:
if (nhanVien instanceof NhanVienFullTime) { // Thực hiện ép kiểu tường minh }
Ứng Dụng Của Ép Kiểu Tường Minh
Tuy ép kiểu tường minh không được sử dụng rộng rãi trong thực tế, nhưng vẫn có một số trường hợp mà nó hữu ích. Ví dụ, khi bạn muốn sử dụng một phương thức chỉ có trong lớp con, bạn có thể sử dụng ép kiểu tường minh để thực hiện điều này.
public class NhanVienFullTime extends NhanVien { private float thuong; public void setThuong(float thuong) { this.thuong = thuong; } @Override public void xuatThongTin() { System.out.println("Nhân viên toàn thời gian: " + ten + ", thưởng: " + thuong); } } public class MainClass { public static void main(String[] args) { NhanVien[] mangNhanVien = new NhanVien[3]; mangNhanVien[0] = new NhanVienFullTime(); mangNhanVien[1] = new NhanVien(); mangNhanVien[2] = new NhanVien(); for (int i = 0; i mangNhanVien.length; i++) { mangNhanVien[i].setTen("Nhan viên " + i); if (mangNhanVien[i] instanceof NhanVienFullTime) { NhanVienFullTime nhanVienFullTime = (NhanVienFullTime) mangNhanVien[i]; nhanVienFullTime.setThuong(100); } } for (NhanVien nhanVien : mangNhanVien) { nhanVien.xuatThongTin(); } } }
Trong ví dụ trên, chúng ta có một mảng các đối tượng NhanVien
, trong đó một đối tượng được ép kiểu tường minh thành NhanVienFullTime
. Bằng cách này, chúng ta có thể sử dụng phương thức đặc biệt của lớp con NhanVienFullTime
khi cần thiết.
Điều quan trọng khi sử dụng ép kiểu tường minh là phải kiểm tra kiểu dữ liệu trước khi thực hiện ép kiểu để tránh lỗi ClassCastException
.
Đó là những kiến thức cơ bản về ép kiểu trong OOP. Hy vọng bài viết này đã giúp bạn hiểu rõ hơn về cách hoạt động của nó và biết cách áp dụng trong thực tế. Hãy tiếp tục đọc các bài viết tiếp theo để khám phá những khái niệm cốt lõi khác của OOP.