Lớp bên trong, lớp lồng nhau và lớp ẩn danh
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 49 trên 87.
Java cho phép bạn định nghĩa lớp bên trong các lớp khác. Những lớp bên trong này giúp tổ chức code bằng cách nhóm các chức năng liên quan lại với nhau và có thể truy cập trực tiếp vào các thành viên của lớp bên ngoài.
Lớp lồng bên trong được định nghĩa là thành viên của một lớp khác. Lớp này có quyền truy cập vào tất cả các trường và phương thức của lớp bên ngoài, kể cả các trường và phương thức riêng tư:
class Outer {
private String message = "Hello";
class Inner {
void display() {
System.out.println(message); // Có thể truy cập trường private
}
}
}
// Cách sử dụng
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.display(); // In ra: HelloLớp bên trong cục bộ được định nghĩa bên trong một phương thức. Lớp này chỉ có thể được dùng trong phương thức đó và có thể truy cập các biến cục bộ về cơ bản là hằng số:
void process() {
String prefix = "Result: ";
class LocalHelper {
void print(int value) {
System.out.println(prefix + value);
}
}
new LocalHelper().print(42);
}Lớp bên trong ẩn danh là lớp không có tên, được tạo và khởi tạo trong một biểu thức. Lớp này thường được dùng để triển khai giao diện hoặc mở rộng lớp ngay tại chỗ:
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};Lớp bên trong đặc biệt hữu ích khi một lớp chỉ liên quan đến một lớp khác, giúp bạn sắp xếp code gọn gàng và đóng gói.
Thử thách
DễHãy xây dựng một hệ thống giỏ hàng minh họa cả ba loại lớp bên trong. Bạn sẽ tạo một ShoppingCart dùng lớp bên trong lồng nhau để biểu diễn các mặt hàng, lớp bên trong cục bộ để tính mức giảm giá và lớp bên trong ẩn danh để định dạng hóa đơn cuối cùng.
Bạn sẽ sắp xếp code trong ba tệp:
ShoppingCart.java: Tạo lớp bên ngoài quản lý trải nghiệm mua sắm.ShoppingCartcần có một trường riêng tưcustomerName(String).Bên trong
ShoppingCart, hãy định nghĩa một lớp bên trong lồng nhau tên làItemvới hai trường riêng tư:name(String) vàprice(double). Tạo hàm khởi tạo choItemđể khởi tạo cả hai trường và một phương thứcgetDetails()trả về:[name]: $[price]ShoppingCartcần có một hàm khởi tạo nhận tên khách hàng và một phương thức tên làcheckout()nhận mộtItemcùng phần trăm giảm giá (double). Bên trongcheckout():- Tạo một lớp bên trong cục bộ tên là
DiscountCalculatorvới phương thứcapplyDiscount(double originalPrice)trả về giá sau khi áp dụng phần trăm giảm giá. - Dùng lớp cục bộ để tính giá cuối cùng.
- Tạo một lớp bên trong ẩn danh triển khai giao diện
Formatter(bạn sẽ định nghĩa giao diện này) để định dạng hóa đơn. Lớp ẩn danh cần ghi đè phương thứcformat(), trả về một chuỗi hóa đơn gồm nhiều dòng.
Phương thức
checkout()cần trả về chuỗi hóa đơn đã định dạng theo chính xác định dạng sau:--- Receipt --- Customer: [customerName] Item: [item details from getDetails()] Discount: [discount]% Final Price: $[calculated price]- Tạo một lớp bên trong cục bộ tên là
Formatter.java: Định nghĩa một giao diện đơn giản với một phương thức duy nhất:String format(). Lớp bên trong ẩn danh của bạn sẽ triển khai giao diện này bên trong phương thức checkout.Main.java: Hãy bắt đầu trải nghiệm mua sắm! Bạn sẽ nhận ba đầu vào: tên khách hàng (String), tên mặt hàng (String) và giá mặt hàng (double). Phần trăm giảm giá cố định là10.0.Tạo một
ShoppingCartvới tên khách hàng. Sau đó tạo mộtItembằng cú pháp tham chiếu đến lớp bên ngoài:cart.new Item(name, price). Gọicheckout()với mặt hàng và mức giảm giá, rồi in ra hóa đơn được trả về.
Bạn sẽ nhận ba đầu vào theo thứ tự: tên khách hàng, tên mặt hàng và giá mặt hàng.
Ví dụ, nếu các đầu vào là Alice, Laptop và 1000.0, giá cuối cùng sau khi giảm 10% sẽ là 900.0.
Hãy để ý mỗi loại lớp bên trong có một mục đích khác nhau: lớp bên trong lồng nhau nhóm dữ liệu liên quan với lớp bên ngoài, lớp bên trong cục bộ đóng gói logic chỉ cần dùng trong một phương thức, còn lớp bên trong ẩn danh cung cấp cách triển khai giao diện nhanh chóng, chỉ dùng một lần!
Tự làm thử
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Đọc các đầu vào
String customerName = scanner.nextLine();
String itemName = scanner.nextLine();
double itemPrice = scanner.nextDouble();
double discount = 10.0;
// TODO: Tạo ShoppingCart với tên khách hàng
// TODO: Tạo Item bằng: cart.new Item(itemName, itemPrice)
// TODO: Gọi checkout() với mặt hàng và mức giảm giá, rồi in ra kết quả
}
}
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