Giải pháp chuyển đổi số trọn gói cho doanh nghiệp Việt
Chắc các bạn còn nhớ, chúng ta đã nói về ép kiểu ở bài học số 6. Và hiển nhiên bạn đã biết khái niệm ép kiểu là gì rồi, bài này chúng ta không cần nhắc đến.
Và ép kiểu ở bài học đó chính là ép kiểu trên các dữ liệu nguyên thủy. Việc ép kiểu lúc bấy giờ được phân biệt làm hai trường hợp riêng biệt, đó là ép kiểu ngầm định và ép kiểu tường minh. Xem ra thì ép kiểu mà bạn đã biết cũng không có gì phức tạp lắm nhỉ. Vậy thì hôm nay, khi biết về OOP, chúng ta sẽ xem việc ép kiểu trên các dữ liệu không-phải-nguyên-thủy sẽ trông như thế nào nhé.
Vâng, trong OOP, chúng ta cũng có thể phân loại ép kiểu ra làm hai dạng, tường minh và ngầm định.
Ép kiểu ngầm định với các lớp trong OOP cũng tương tự như ép kiểu ngầm định với các biến theo kiểu dữ liệu nguyên thủy. Đó là nếu như không có sự mất mát dữ liệu, hay có thể nới rộng khả năng lưu trữ của dữ liệu, thì xem như hệ thống sẽ hoàn toàn ngầm định giúp chúng ta ép kiểu.
Thế nhưng, với OOP, chúng ta có hai trường hợp ép kiểu ngầm định sau đây, mời bạn cùng đi đến hai mục nhỏ bên dưới.
Thực sự vấn đề này cũng quen thuộc thôi. Nó như là bạn khai báo hai biến int a và int b, rồi bạn gán b = a, và kết quả là a và b sẽ có cùng một giá trị. Với OOP thì sao, cũng vậy, nó sẽ như là bạn khai báo NhanVien a và NhanVien b, rồi bạn gán b = a, kết quả là b với a sẽ mang cùng một nội dung.
Tuy vậy nhưng cũng không hẳn là vậy. Với việc gán hai biến nguyên thủy int vào int như trên thì hết sức bình thường. Nhưng việc bạn gán hai đối tượng NhanVien vào NhanVien thì được xem như một phép ép kiểu, vì sao vậy, chúng ta thử đến ví dụ bên dưới đây.
Giả sử có một lớp NhanVien cực kỳ đơn giản, lớp này chỉ có một thuộc tính ten và các phương thức getter/setter cho nó, và một phương thức giúp xuất thông tin ten ra console.
Đến phương thức main(). Chúng ta sẽ khai báo hai đối tượng nhanVien1 và nhanVien2 từ lớp NhanVien này, và bạn hãy xem hệ thống gán, hay ép kiểu ngầm định hai đối tượng này cho nhau như thế nào với các dòng code sau nhé.
Bạn chú ý vào comment “xuất thông tin hai đối tượng lần 3”. Trước khi xuất thông tin ở lần này cho cả hai đối tượng nhanVien1 và nhanVien2, thì chỉ có mỗi nhanVien2 là có sự thay đổi giá trị đến thuộc tính ten mà thôi.
// Thay đổi thông tin giá trị ten bên trong nhanVien2nhanVien2.setTen("Khải");
Vậy cớ sự làm sao mà ten ở nhanVien1 cũng bị chung số phận? Vì bạn nên nhớ rằng, trong OOP, khi bạn thực hiện phép gán hai đối tượng cho nhau, như ví dụ là phép gán
, thì hệ thống sẽ ép kiểu dữ liệu, và cả tham chiếu, của cả hai đối tượng vào nhau, làm cho chúng giờ đây trở thành một. Điều này khác hoàn toàn với việc sử dụng phép gán ở kiểu dữ liệu nguyên thủy, bạn hãy ghi nhớ điều này nhé.
Cũng giống như ép kiểu ngầm định dạng mở rộng khả năng lưu trữ ở các kiểu dữ liệu nguyên thủy, hệ thống sẽ tự thực hiện ép kiểu khi mà 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, như từ int sang float chẳng hạn. Thì hệ thống cũng sẽ làm tương tự vậy với OOP, nếu có sự chuyển đổi dữ liệu từ lớp con sang lớp cha.
Giờ giả sử chúng ta xây dựng một lớp con của NhanVien, chính là lớp NhanVienFullTime. Lớp con này có override lại phương thức xuatThongTin() của lớp cha. Code của lớp NhanVienFullTime này như sau.
Rồi. Như vậy chúng ta cùng xem ở phương thức main(), xem việc ép kiểu ngầm định diễn ra như thế nào nhé. Bạn có thể thấy rằng kết cục của phép gán, và ép kiểu giữa các lớp trong OOP đều mang đến kết quả là hai đối tượng sẽ trở thành một (vì cùng một tham chiếu). Sau khi gán, hễ bạn thay đổi giá trị của một lớp, thì lớp cùng trong phép gán kia cũng sẽ bị thay đổi giá trị tương tự.
Bạn hoàn toàn có thể đoán được khi nào chúng ta cần thiết phải ép kiểu tường minh rồi đúng không nào. Đó là khi mà hệ thống phát hiện thấy bạn đang muốn chuyển dữ liệu từ kiểu dữ liệu có kích thước lớn hơn sang kiểu dữ liệu có kích thước nhỏ hơn. Với OOP thì từ lớp cha sang lớp con.
Chúng ta đến với ví dụ với phép gán ngược lại với ví dụ ngay trên đây.
Tại sao code lần này ít thế. Thực ra bạn không nên code nữa, có lỗi xảy ra ở dòng cuối cùng rồi. Dù cho sau khi bạn code, trình biên dịch không hề báo lỗi, nhưng nếu ngay bây giờ bạn thực thi ứng dụng, sẽ nhận được một exception ClassCastException như sau.

Lỗi này có nghĩa là khi bạn ép kiểu tường minh từ nhanVien về nhanVienFullTime, lúc này trình biên dịch vẫn thấy có lý, vì chúng quan hệ cha con mà. Nhưng khi thực thi ở môi trường thực tế, thì lớp nhanVien vốn dĩ không biết đến nhanVienFullTime là gì, nên không thể thực hiện việc ép kiểu được.
Vậy thì ép kiểu tường minh đối với OOP là như thế nào? Thực ra, nếu như chúng ta có áp dụng tính đa hình, tức là ở lúc nào đó nhanVien phải “đặt mình” vào vai trò là một nhanVienFullTime, thì chúng mới hiểu nhau khi “cùng nhau sánh bước trong đường đời” phía trước. Code như thế này sẽ chạy tốt.
Bạn đã thấy một ví dụ dẫn đến một exception có tên ClassCastException được tung ra ở ví dụ trên kia của mình. Đó là một cảnh báo nhãn tiền cho việc sử dụng ép kiểu tường minh đối với OOP đấy. Nó cho thấy nếu như bạn sử dụng viện ép kiểu không khéo, dẫn đến việc ép sai lớp của đối tượng, ứng dụng của bạn sẽ chết, hay crash.
Để đảm bảo việc ép kiểu tường minh được diễn ra an toàn, một yêu cầu đối với các lập trình viên là phải luôn dùng instanceof để kiểm tra đối tượng có thuộc lớp cụ thể nào không trước khi chính thức sử dụng ép kiểu.
Cách sử dụng instanceof cũng khá đơn giản, bạn hãy nhìn vào code sau sẽ rõ cách dùng. Mình thêm dòng if vào ví dụ bạn vừa code trên đây.
Nếu bạn đọc đến đây của bài viết, mình chắc bạn đã nắm được thế nào là ép kiểu ngầm định và thế nào là ép kiểu tường minh rồi. Thế nhưng cũng sẽ có bạn thắc mắc rằng những sự ép kiểu này sẽ được sử dụng ở tình huống nào trong thực tế?
Nếu như với ép kiểu ngầm định thì với việc gán các đối tượng cho nhau là đã thực hiện một phép ép kiểu dễ dàng và an toàn rồi, nên mình sẽ không nói đến việc ứng dụng của kiểu ép này. Nhưng với ép kiểu tường minh, ứng dụng của nó khá hẹp, nếu muốn nói rằng hầu như bạn cũng chả cần đến. Nhưng nếu như bạn muốn nhìn code được tường minh hơn, hay muốn chỉ định rõ ràng phương thức mà chỉ có ở lớp con, khi đó bạn có thể dùng đến ép kiểu tường minh về lớp con như ví dụ sau.
Đầu tiên chúng ta phải làm cho lớp con NhanVienFullTime có phương thức mà lớp cha nó không có, như sau mình thêm vào thuộc tính thuong (thưởng) và phương thức setThuong().
Ở main() lần này chúng ta khai báo mảng các NhanVien có tên là mangNhanVien. Trong đó mangNhanVien[0] mang tính đa hình để có thể trở thành NhanVienFullTime, các thành phần mangNhanVien còn lại đều là kiểu lớp cha NhanVien.
Bạn thấy trong vòng lặp đầu tiên, nếu mong muốn rằng mangNhanVien[i] nếu là NhanVienFullTime, thì ngoài việc setTen() ra nó còn phải gọi thêm setThuong() nữa. Khi này ép kiểu tường minh phát huy tác dụng.
Kết quả thực thi chương trình của ví dụ trên đây.
Chưa có câu hỏi nào cho bài này.