Chuyển kiểu lên và chuyển kiểu xuống
Thuộc phần Lập trình hướng đối tượng trong hành trình Java của Coddy. Bài học 30 trên 87.
Trong bài học trước, bạn đã thấy biến kiểu lớp cha có thể chứa một đối tượng lớp con như thế nào: Animal myPet = new Dog(). Phép gán này được gọi là upcasting, và nó diễn ra tự động vì Dog luôn là một Animal.
Upcasting đi lên trong hệ thống phân cấp kế thừa. Thao tác này diễn ra ngầm và luôn an toàn:
Dog dog = new Dog();
Animal animal = dog; // Ép kiểu lên tự độngKhi ép kiểu lên, bạn chỉ có thể truy cập các phương thức được định nghĩa trong lớp cha. Các phương thức riêng của lớp con sẽ bị ẩn, dù bên dưới đối tượng vẫn là một Dog.
Ép kiểu xuống chuyển đổi theo hướng ngược lại, chuyển một tham chiếu lớp cha về kiểu lớp con. Việc này cần ép kiểu tường minh vì không phải lúc nào cũng an toàn:
Animal animal = new Dog(); // Ép kiểu lên
Dog dog = (Dog) animal; // Ép kiểu xuống: cần ép kiểu tường minh
dog.bark(); // Giờ bạn có thể truy cập các phương thức của DogÉp kiểu xuống có rủi ro. Nếu đối tượng không thực sự thuộc kiểu mà bạn đang ép sang, Java sẽ ném ra ClassCastException khi chạy chương trình:
Animal animal = new Cat();
Dog dog = (Dog) animal; // Biên dịch thành công, nhưng bị lỗi khi chạy!Trình biên dịch không thể phát hiện lỗi này vì nó chỉ thấy các kiểu đã khai báo. Chỉ khi chương trình chạy, ta mới biết kiểu thực tế của đối tượng. Trong bài học tiếp theo, bạn sẽ học cách kiểm tra kiểu của đối tượng một cách an toàn trước khi ép kiểu xuống.
Thử thách
DễHãy xây dựng một hệ thống trình phát đa phương tiện thể hiện cách ép kiểu lên và ép kiểu xuống trong thực tế. Bạn sẽ tạo một hệ thống phân cấp các loại phương tiện và luyện tập chuyển đổi giữa tham chiếu của lớp cha và lớp con.
Bạn sẽ sắp xếp code trong bốn tệp:
Media.java: Tạo lớp cơ sở dùng chung cho tất cả các loại phương tiện. Mỗi mục phương tiện có một trườngtitle(String). Thêm hàm khởi tạo để khởi tạo trường này, phương thức gettergetTitle()và phương thứcplay()để in ra:Playing: [title]Song.java: Tạo một lớp kế thừa Media. Bài hát có thêm trườngartist(String). Dùngsuperđể khởi tạo lớp cha. Ghi đèplay()để in ra:Playing song: [title] by [artist]. Thêm phương thứcshowArtist()để in ra:Artist: [artist]Podcast.java: Tạo một lớp khác kế thừa Media. Podcast có trườngepisode(int). Ghi đèplay()để in ra:Playing podcast: [title] - Episode [episode]. Thêm phương thứcshowEpisode()để in ra:Episode: [episode]Main.java: Minh họa cách ép kiểu lên và ép kiểu xuống với các loại phương tiện. Bạn sẽ nhận bốn đầu vào: tiêu đề bài hát, tên nghệ sĩ, tiêu đề podcast và số tập.Trước tiên, tạo một Song và ép kiểu lên thành tham chiếu Media. Gọi
play()trên tham chiếu này để xem tính đa hình lúc chạy hoạt động.Tiếp theo, ép kiểu xuống tham chiếu Media đó trở lại thành Song và gọi
showArtist()để truy cập phương thức riêng của lớp con.Sau đó, tạo một Podcast và ép kiểu lên thành tham chiếu Media. Gọi
play()trên tham chiếu này.Cuối cùng, ép kiểu xuống tham chiếu Media đó trở lại thành Podcast và gọi
showEpisode().
Bạn sẽ nhận bốn đầu vào: tiêu đề bài hát (String), tên nghệ sĩ (String), tiêu đề podcast (String) và số tập (int).
Đầu ra gồm bốn dòng: hai dòng cho bài hát (kết quả phát và thông tin nghệ sĩ) và hai dòng cho podcast (kết quả phát và thông tin tập). Hãy chú ý cách ép kiểu lên cho phép bạn xử lý đồng nhất các loại phương tiện khác nhau, còn ép kiểu xuống cho phép bạn truy cập những tính năng riêng của chúng!
Tự làm thử
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Đọc dữ liệu đầu vào
String songTitle = scanner.nextLine();
String artistName = scanner.nextLine();
String podcastTitle = scanner.nextLine();
int episodeNumber = scanner.nextInt();
// TODO: Tạo một Song và ép kiểu lên thành tham chiếu Media
// TODO: Gọi play() qua tham chiếu Media (minh họa tính đa hình)
// TODO: Ép kiểu tham chiếu Media xuống lại thành Song
// TODO: Gọi showArtist() qua tham chiếu Song
// TODO: Tạo một Podcast và ép kiểu lên thành tham chiếu Media
// TODO: Gọi play() qua tham chiếu Media
// TODO: Ép kiểu tham chiếu Media xuống lại thành Podcast
// TODO: Gọi showEpisode() qua tham chiếu Podcast
}
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Kiến thức cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcTrường (thuộc tính)Phương thức khởi tạoNạp chồng phương thức khởi tạoÔn tập: Máy tính đơn giản4Kế thừa
Kế thừa cơ bản (extends)Từ khóa superGhi đè phương thức (@Override)Chuỗi gọi hàm khởi tạoLớp ObjectKế thừa đơn và đa cấpVì sao không hỗ trợ kế thừa nhiều lớpÔn tập: Phân cấp lớp Employee7Phương thức đặc biệt và lớp Object
Phương thức toString()equals() và hashCode()Phương thức clone()compareTo() và ComparableGiao diện ComparatorÔn tập: sắp xếp tùy chỉnh2Bộ điều chỉnh quyền truy cập và đóng gói
Tổng quan về các mức truy cậpPhương thức getter và setterẨn thông tinTừ khóa finalÔn tập: Trình quản lý tài khoản ngân hàng5Tính đa hình
Kiến thức cơ bản về nạp chồng phương thứcGhi đè phương thức (thời gian chạy)Chuyển kiểu lên và chuyển kiểu xuốngToán tử instanceofLớp và phương thức trừu tượngÔn tập: máy tính hình học8Khái niệm OOP nâng cao
Kết hợp và kế thừaKết tập và hợp thànhLớp bên trong, lớp lồng nhau và lớp ẩn danhKiểu enum và phương thức enumBản ghi (Java 16+)Lớp sealed (Java 17+)3Thuộc tính lớp và thành viên tĩnh
Biến của instance và biến tĩnhPhương thức tĩnhKhối tĩnhHằng số (static final)Ôn tập: bộ đếm và tiện ích6Giao diện và lớp trừu tượng
Giới thiệu về giao diệnTriển khai giao diệnTriển khai nhiều giao diệnPhương thức mặc định và tĩnh trong giao diệnLớp trừu tượng và giao diệnGiao diện hàmÔn tập: hệ thống thanh toán9Kiểu tổng quát
Giới thiệu về kiểu tổng quátLớp tổng quátPhương thức tổng quátTham số kiểu có giới hạnKý tự đại diện (?, extends, super)Ôn tập: vùng chứa tổng quát12Mẫu thiết kế, phần 2
Mẫu CommandMẫu AdapterMẫu DecoratorMẫu Template MethodMẫu StateMẫu CompositeMẫu IteratorTự luyện tập: Trình biên dịch Java online