Bài toán: Kiểm tra một chuỗi có tuân theo quy tắc đặt tên identifier (biến, phương thức, lớp) trong Java hay không. Lưu ý: "Valid keyword" trong tiêu đề gốc hơi nhầm lẫn – keyword là từ khóa dành riêng (if, for, class...), không thể dùng làm identifier. Code thực tế kiểm tra valid identifier.
Quy tắc Java Identifier
- Ký tự đầu: Chữ cái (a-z, A-Z), underscore (
_), dollar sign ($). Không được bắt đầu bằng số. - Ký tự tiếp theo: Chữ cái, số (0-9), underscore, dollar sign.
- Không được là reserved keywords (50+ từ: if, else, class, int, static...).
- Không được là
true,false,null(literals). - Độ dài: không giới hạn (thực tế hạn chế bởi JVM/class file format ~64KB).
- Unicode: Hỗ trợ chữ cái Unicode (Letter category), không chỉ ASCII.
Cách 1: Regex (Đơn giản, ASCII only)
public class IdentifierValidator {
// Regex ASCII: chữ cái đầu, tiếp theo chữ/số/_/$
private static final String IDENTIFIER_REGEX = "^[a-zA-Z_$][a-zA-Z0-9_$]*$";
// Danh sách keyword (Java 17)
private static final Set KEYWORDS = Set.of(
"abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const",
"continue", "default", "do", "double", "else", "enum", "extends", "final", "finally", "float",
"for", "goto", "if", "implements", "import", "instanceof", "int", "interface", "long", "native",
"new", "package", "private", "protected", "public", "return", "short", "static", "strictfp", "super",
"switch", "synchronized", "this", "throw", "throws", "transient", "try", "void", "volatile", "while",
"_", "record", "sealed", "non-sealed", "permits", "yield", "module", "open", "opens", "requires",
"exports", "to", "uses", "provides", "with", "transitive"
);
public static boolean isValidIdentifier(String str) {
if (str == null || str.isEmpty()) return false;
if (!str.matches(IDENTIFIER_REGEX)) return false;
if (KEYWORDS.contains(str)) return false;
return true;
}
public static void main(String[] args) {
String[] tests = {"validName", "_private", "$dollar", "2invalid", "class", "null", "valid$name123", "tên_việt"};
for (String t : tests) {
System.out.printf("%-15s : %s%n", t, isValidIdentifier(t));
}
}
}
Cách 2: Character API (Hỗ trợ Unicode chuẩn Java)
public static boolean isValidIdentifierUnicode(String str) {
if (str == null || str.isEmpty()) return false;
if (KEYWORDS.contains(str)) return false;
int cp = str.codePointAt(0);
if (!Character.isJavaIdentifierStart(cp)) return false;
for (int i = Character.charCount(cp); i < str.length(); i += Character.charCount(cp)) {
cp = str.codePointAt(i);
if (!Character.isJavaIdentifierPart(cp)) return false;
}
return true;
}
Character.isJavaIdentifierStart(int codePoint): Kiểm tra ký tự bắt đầu hợp lệ (Unicode Letter, _, $).Character.isJavaIdentifierPart(int codePoint): Kiểm tra ký tự tiếp theo hợp lệ (Letter, Digit, _, $, combining marks...).- Xử lý đúng supplementary characters (code point > 0xFFFF) bằng
codePointAtvàcharCount.
Cách 3: Compiler API (Java 9+) – Kiểm tra thực tế
import javax.tools.*;
public static boolean isValidIdentifierCompiler(String name) {
if (name == null || name.isEmpty()) return false;
if (KEYWORDS.contains(name)) return false;
// Dùng JavaCompiler để parse một snippet dummy
String code = "public class Test { void " + name + "() {} }";
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector diagnostics = new DiagnosticCollector<>();
JavaFileObject file = new SimpleJavaFileObject(URI.create("string:///Test.java"), JavaFileObject.Kind.SOURCE) {
@Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { return code; }
};
CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, List.of(file));
return task.call(); // true nếu compile thành công → identifier hợp lệ
}
So sánh
| Cách | Ưu điểm | Nhược điểm |
|---|---|---|
| Regex ASCII | Nhanh, đơn giản, không phụ thuộc JDK | Không hỗ trợ Unicode identifier (tên tiếng Việt, tiếng Nhật...) |
| Character API | Chuẩn Java, hỗ trợ Unicode đầy đủ, nhanh | Cần JDK runtime |
| Compiler API | Chính xác 100% theo language spec | Nặng, chậm, cần tools.jar (JDK không JRE) |