Lớp tổng quát
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 54 trên 87.
Giờ bạn đã hiểu vì sao kiểu tổng quát hữu ích, hãy tạo lớp tổng quát của riêng mình. Một lớp tổng quát khai báo một hoặc nhiều tham số kiểu trong dấu ngoặc nhọn sau tên lớp, rồi có thể dùng các tham số đó trong toàn bộ lớp.
class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
}T là tham số kiểu: một chỗ giữ chỗ được thay bằng kiểu dữ liệu thực tế khi bạn tạo một đối tượng. Bạn có thể dùng tên bất kỳ, nhưng người ta thường dùng một chữ cái viết hoa: T cho Type, E cho Element, K cho Key, V cho Value.
Khi tạo một lớp tổng quát, bạn chỉ định kiểu dữ liệu cụ thể trong dấu ngoặc nhọn:
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get(); // Không cần ép kiểu
Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer num = intBox.get();Hãy chú ý đến <> (toán tử kim cương) ở phía bên phải: Java suy ra kiểu dữ liệu từ khai báo. Bạn cũng có thể dùng nhiều tham số kiểu cho các lớp cần làm việc với nhiều kiểu dữ liệu:
class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Pair<String, Integer> pair = new Pair<>("age", 25);Thử thách
DễHãy xây dựng một hệ thống đăng ký quà tặng để thể hiện sức mạnh của các lớp tổng quát. Bạn sẽ tạo một hệ thống lưu trữ linh hoạt, có thể chứa mọi loại quà tặng, cùng với một danh sách đăng ký ghép tên người nhận với thông tin quà tặng bằng nhiều tham số kiểu dữ liệu.
Bạn sẽ sắp xếp code trong ba tệp:
GiftBox.java: Tạo một lớp tổng quát có thể gói mọi loại quà tặng.GiftBox<T>cần có một trường riêng tưitemthuộc kiểuTvà một trường riêng tưwrappedthuộc kiểu boolean, có giá trị ban đầu làfalse. Thêm hàm khởi tạo nhận quà tặng, phương thứcgetItem()trả về quà tặng, phương thứcwrap()đặtwrappedthành true và phương thứcisWrapped()trả về trạng thái gói quà. Thêm phương thứcgetStatus()trả về[item] (wrapped)hoặc[item] (unwrapped)tùy theo trạng thái gói quà.Registry.java: Tạo một lớp tổng quát có hai tham số kiểu dữ liệu, ghép người nhận với quà tặng của họ.Registry<K, V>cần có các trường riêng tưrecipientthuộc kiểuKvàgiftthuộc kiểuV. Thêm hàm khởi tạo nhận cả hai giá trị, các phương thức gettergetRecipient()vàgetGift(), cùng phương thứcgetEntry()trả về:[recipient] -> [gift]Main.java: Hoàn thiện hệ thống đăng ký quà tặng! Bạn sẽ nhận ba đầu vào: tên người nhận (String), mô tả quà tặng (String) và giá trị quà tặng (số nguyên biểu thị số đô la).Đầu tiên, tạo một
GiftBox<String>chứa mô tả quà tặng. Gói quà bằng phương thứcwrap(), sau đó in trạng thái của quà.Tiếp theo, tạo một
GiftBox<Integer>chứa giá trị quà tặng (tương ứng với số tiền trên thẻ quà tặng). Đừng gói món quà này, hãy in trạng thái để cho thấy quà chưa được gói.Cuối cùng, tạo một
Registry<String, String>để ghép tên người nhận với mô tả quà tặng, rồi in mục đăng ký.
Bạn sẽ nhận ba đầu vào theo thứ tự: tên người nhận, mô tả quà tặng và giá trị quà tặng (dưới dạng số nguyên).
Hãy để ý cách cùng một lớp GiftBox hoạt động trơn tru với cả kiểu String và Integer, đó chính là ưu điểm của kiểu tổng quát! Lớp Registry cho thấy nhiều tham số kiểu dữ liệu giúp bạn tạo các cặp linh hoạt giữa hai kiểu bất kỳ.
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 recipientName = scanner.nextLine();
String giftDescription = scanner.nextLine();
int giftValue = scanner.nextInt();
// TODO: Tạo một GiftBox<String> chứa mô tả món quà
// TODO: Gói món quà bằng phương thức wrap()
// TODO: In ra trạng thái của nó bằng getStatus()
// TODO: Tạo một GiftBox<Integer> chứa giá trị món quà
// TODO: Đừng gói món quà này, hãy in ra trạng thái của nó (phải hiển thị là chưa được gói)
// TODO: Tạo một Registry<String, String> ghép người nhận với mô tả món quà
// TODO: In ra mục đăng ký bằng getEntry()
}
}
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