1. Vấn đề của instanceof kiểu cổ điển
Hãy bắt đầu với một chút hoài niệm. Trước Java 16, khi bạn cần kiểm tra kiểu của đối tượng rồi sử dụng nó như kiểu đó, bạn phải viết một mẫu dài dòng với kiểm tra instanceof và ép kiểu tường minh:
Object obj = ...; // một đối tượng nào đó
if (obj instanceof String) {
String s = (String) obj;
System.out.println("Độ dài chuỗi: " + s.length());
}
Nhàm chán nhưng thường gặp
Mẫu này xuất hiện rất thường xuyên — bộ xử lý sự kiện, làm việc với các collection không đồng nhất, business logic với kế thừa, v.v.
Vì sao bất tiện
- Lặp mã — phải ghi kiểu hai lần: cả trong instanceof và trong ép kiểu.
- Nguy cơ lỗi — dễ vô tình ép về sai kiểu và nhận ClassCastException.
- Mã “ồn ào” — giảm tính dễ đọc, đặc biệt khi có nhiều kiểm tra.
2. Pattern Matching cho instanceof: Cú pháp mới
Trong Java 16 đã thêm pattern matching cho instanceof. Giờ bạn kiểm tra kiểu và đồng thời khai báo biến đúng kiểu, biến này chỉ khả dụng bên trong nhánh if.
Cú pháp mới
if (obj instanceof String s) {
// s đã là String!
System.out.println("Độ dài chuỗi: " + s.length());
}
- Sau kiểu trong instanceof ghi tên biến mới: String s.
- Biến chỉ khả dụng bên trong khối nơi điều kiện đúng.
- Không cần viết ép kiểu (String) — trình biên dịch đảm bảo tất cả.
Điều kỳ diệu là gì? Trình biên dịch kiểm tra kiểu và, nếu phù hợp, “mở” biến với kiểu cần thiết. Nếu không, khối đơn giản là không được thực thi.
Trông như thế nào trong mã thực tế
Object obj = "Xin chào, Java 16+!";
if (obj instanceof String s) {
System.out.println("Chữ hoa: " + s.toUpperCase());
} else {
System.out.println("Không phải chuỗi!");
}
3. An toàn và dễ đọc: vì sao điều này tuyệt
Loại bỏ lỗi ClassCastException
Object obj = 123;
// String s = (String) obj; // Bùm! Ngoại lệ.
Với pattern matching, biến chỉ được tạo khi kiểm tra kiểu thành công — không có ép kiểu “sai”.
Mã ngắn gọn và dễ hiểu hơn
Hai dòng thành một. Ít mã “ồn ào” hơn — dễ đọc và bảo trì.
Ví dụ: xử lý nhiều kiểu
public static void printInfo(Object obj) {
if (obj instanceof String s) {
System.out.println("Chuỗi có độ dài " + s.length());
} else if (obj instanceof Integer i) {
System.out.println("Số nguyên: " + (i + 1));
} else {
System.out.println("Kiểu không xác định: " + obj);
}
}
4. Ví dụ sử dụng và các lưu ý
Kiểm tra nhiều kiểu
Object value = ...;
if (value instanceof String s) {
System.out.println("Đây là chuỗi: " + s);
} else if (value instanceof Number n) {
System.out.println("Đây là số: " + n);
} else {
System.out.println("Thứ gì đó khác: " + value);
}
Sử dụng với null
Nếu đối tượng bằng null, thì instanceof luôn trả về false — sẽ không có NPE, nhưng khối sẽ không chạy.
Object obj = null;
if (obj instanceof String s) {
// Khối này KHÔNG BAO GIỜ chạy nếu obj == null
System.out.println("Chuỗi: " + s);
} else {
System.out.println("obj là null hoặc không phải chuỗi");
}
Pattern matching với kế thừa
class Animal {}
class Dog extends Animal {
void bark() { System.out.println("Gâu!"); }
}
Animal a = new Dog();
if (a instanceof Dog d) {
d.bark(); // Có thể gọi các phương thức Dog mà không cần ép kiểu!
}
Nhắc lại, thứ tự kiểm tra rất quan trọng: hãy đi từ cụ thể đến tổng quát, nếu không các kiểm tra “hẹp” có thể không khớp.
So sánh: cách cũ và cách mới
| Cách cũ | Cách mới (pattern matching) |
|---|---|
|
|
5. Hạn chế của pattern matching cho instanceof
- Phạm vi của biến. Biến từ mẫu chỉ nhìn thấy trong nhánh nơi kiểm tra là đúng.
if (obj instanceof String s) {
System.out.println(s); // s khả dụng
}
// System.out.println(s); // Lỗi! s không nhìn thấy ở đây
- Không thể khai báo nhiều biến kiểu khác nhau trong cùng một điều kiện.
// Lỗi biên dịch:
if (obj instanceof String s || obj instanceof Integer i) {
// ...
}
- Cần Java 16+. Trên các phiên bản JDK cũ hơn, cú pháp mới không được hỗ trợ.
6. Ví dụ thực tế (dựa trên một ứng dụng đơn giản)
Hãy hình dung một Task Manager đơn giản với nhiều loại nhiệm vụ.
Các lớp
class Task {
String title;
public Task(String title) { this.title = title; }
}
class BugTask extends Task {
int severity;
public BugTask(String title, int severity) {
super(title);
this.severity = severity;
}
}
class FeatureTask extends Task {
String feature;
public FeatureTask(String title, String feature) {
super(title);
this.feature = feature;
}
}
Xử lý với pattern matching
public static void processTask(Task t) {
if (t instanceof BugTask bug) {
System.out.println("Bug: " + bug.title + ", mức độ nghiêm trọng: " + bug.severity);
} else if (t instanceof FeatureTask feature) {
System.out.println("Tính năng: " + feature.title + ", mô-đun: " + feature.feature);
} else if (t instanceof Task task) {
System.out.println("Nhiệm vụ thường: " + task.title);
}
}
7. Những lưu ý hữu ích
Bảng: Pattern Matching cho instanceof — ưu và nhược
| Ưu điểm | Nhược điểm/Hạn chế |
|---|---|
| Ít mã hơn, dễ đọc hơn | Yêu cầu Java 16+ |
| Không có rủi ro ClassCastException | Biến chỉ nhìn thấy trong khối if |
| An toàn kiểu ở mức trình biên dịch | Không hoạt động cho nhiều kiểu trong cùng một điều kiện |
| Thuận tiện khi xử lý kế thừa | Có thể không được hỗ trợ trong IDE/bản build cũ |
| Phù hợp với mọi lớp |
Sơ đồ trực quan: pattern matching cho instanceof hoạt động như thế nào
+---------------------------+
| Object obj |
+---------------------------+
|
v
if (obj instanceof Type t)
|
Có Không
(true) (false)
| |
v v
t khả dụng t không tồn tại
(mã) (không truy cập)
8. Lỗi thường gặp khi dùng pattern matching cho instanceof
Lỗi #1: Cố dùng biến ngoài khối if. Bạn khai báo biến trong mẫu, ra khỏi khối — biến không còn nữa. Trình biên dịch sẽ thắc mắc: “s là ai vậy?”
Lỗi #2: Kỳ vọng rằng instanceof hoạt động với null. Nếu đối tượng bằng null, điều kiện instanceof luôn sai, biến sẽ không được tạo. Hãy xử lý null riêng khi cần.
Lỗi #3: Dùng cú pháp mới trên phiên bản JDK/IDE cũ. Trên Java 11 trở xuống, pattern matching cho instanceof không khả dụng — bạn sẽ nhận lỗi cú pháp. Hãy kiểm tra phiên bản JDK.
Lỗi #4: Nhầm lẫn về phạm vi. Biến từ mẫu chỉ khả dụng trong nhánh nơi kiểm tra đúng. Bên ngoài — nó không tồn tại.
Lỗi #5: Mong đợi hỗ trợ nhiều kiểu đồng thời. Không thể khai báo ngay hai biến kiểu khác nhau trong cùng một điều kiện: if (obj instanceof String s || obj instanceof Integer i) — không làm được như vậy. Mỗi kiểu — một kiểm tra riêng.
GO TO FULL VERSION