How can I best check if a string input would be a valid java variable for coding? I'm sure I'm not the first one who is willing to do this. But maybe I'm missing the right keyword to find something useful.
Best would probably be a RegEx, which checks that:
starts with a letter
can then contain digits, letters
can contain some special characters, like '_' (which?)
public static boolean isValidJavaIdentifier(String s) {
if (s.isEmpty()) {
return false;
}
if (!Character.isJavaIdentifierStart(s.charAt(0))) {
return false;
}
for (int i = 1; i < s.length(); i++) {
if (!Character.isJavaIdentifierPart(s.charAt(i))) {
return false;
}
}
return true;
}
EDIT: and, as @Joey indicates, you should also filter out keywords and reserved words.
Use
import javax.lang.model.SourceVersion;
boolean isValidVariableName(CharSequence name) {
return SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name);
}
if you need to check whether a string is a valid Java variable name in the latest version of Java or
import javax.lang.model.SourceVersion;
boolean isValidVariableNameInVersion(CharSequence name, SourceVersion version) {
return SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name, version);
}
if you need to check whether a string is a valid Java variable name in a specific Java version.
For example, underscore became a reserved keyword starting from Java 9, so isValidVariableNameInVersion("_", SourceVersion.RELEASE_9)
returns false
while isValidVariableNameInVersion("_", SourceVersion.RELEASE_8)
returns true
.
How it works
SourceVersion.isIdentifier(CharSequence name)
checks whether or not name is a syntactically valid identifier (simple name) or keyword in the latest source version. !SourceVersion.isKeyword(name)
returns false for keywords. As a result, SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name)
returns true for valid indetifiers and only for them.
The same approach is used in the built-in method SourceVersion.isName(CharSequence name, SourceVersion version)
that checks whether name is a syntactically valid qualified name, which means that it will return true
for strings like "apple.color":
public static boolean isName(CharSequence name, SourceVersion version) {
String id = name.toString();
for(String s : id.split("\\.", -1)) {
if (!isIdentifier(s) || isKeyword(s, version))
return false;
}
return true;
}
Test
import org.junit.jupiter.api.Test;
import javax.lang.model.SourceVersion;
import static org.assertj.core.api.Assertions.assertThat;
public class ValidVariableNameTest {
boolean isValidVariableName(CharSequence name) {
return isValidVariableNameInVersion(name, SourceVersion.RELEASE_8);
}
boolean isValidVariableNameInVersion(CharSequence name, SourceVersion version) {
return SourceVersion.isIdentifier(name) && !SourceVersion.isKeyword(name, version);
}
@Test
void variableNamesCanBeginWithLetters() {
assertThat(isValidVariableName("test")).isTrue();
assertThat(isValidVariableName("e2")).isTrue();
assertThat(isValidVariableName("w")).isTrue();
assertThat(isValidVariableName("привет")).isTrue();
}
@Test
void variableNamesCanBeginWithDollarSign() {
assertThat(isValidVariableName("$test")).isTrue();
assertThat(isValidVariableName("$e2")).isTrue();
assertThat(isValidVariableName("$w")).isTrue();
assertThat(isValidVariableName("$привет")).isTrue();
assertThat(isValidVariableName("$")).isTrue();
assertThat(isValidVariableName("$55")).isTrue();
}
@Test
void variableNamesCanBeginWithUnderscore() {
assertThat(isValidVariableName("_test")).isTrue();
assertThat(isValidVariableName("_e2")).isTrue();
assertThat(isValidVariableName("_w")).isTrue();
assertThat(isValidVariableName("_привет")).isTrue();
assertThat(isValidVariableName("_55")).isTrue();
}
@Test
void variableNamesCannotContainCharactersThatAreNotLettersOrDigits() {
assertThat(isValidVariableName("apple.color")).isFalse();
assertThat(isValidVariableName("my var")).isFalse();
assertThat(isValidVariableName(" ")).isFalse();
assertThat(isValidVariableName("apple%color")).isFalse();
assertThat(isValidVariableName("apple,color")).isFalse();
assertThat(isValidVariableName(",applecolor")).isFalse();
}
@Test
void variableNamesCannotStartWithDigit() {
assertThat(isValidVariableName("2e")).isFalse();
assertThat(isValidVariableName("5")).isFalse();
assertThat(isValidVariableName("123test")).isFalse();
}
@Test
void differentSourceVersionsAreHandledCorrectly() {
assertThat(isValidVariableNameInVersion("_", SourceVersion.RELEASE_9)).isFalse();
assertThat(isValidVariableNameInVersion("_", SourceVersion.RELEASE_8)).isTrue();
assertThat(isValidVariableNameInVersion("enum", SourceVersion.RELEASE_9)).isFalse();
assertThat(isValidVariableNameInVersion("enum", SourceVersion.RELEASE_4)).isTrue();
}
@Test
void keywordsCannotBeUsedAsVariableNames() {
assertThat(isValidVariableName("strictfp")).isFalse();
assertThat(isValidVariableName("assert")).isFalse();
assertThat(isValidVariableName("enum")).isFalse();
// Modifiers
assertThat(isValidVariableName("public")).isFalse();
assertThat(isValidVariableName("protected")).isFalse();
assertThat(isValidVariableName("private")).isFalse();
assertThat(isValidVariableName("abstract")).isFalse();
assertThat(isValidVariableName("static")).isFalse();
assertThat(isValidVariableName("final")).isFalse();
assertThat(isValidVariableName("transient")).isFalse();
assertThat(isValidVariableName("volatile")).isFalse();
assertThat(isValidVariableName("synchronized")).isFalse();
assertThat(isValidVariableName("native")).isFalse();
// Declarations
assertThat(isValidVariableName("class")).isFalse();
assertThat(isValidVariableName("interface")).isFalse();
assertThat(isValidVariableName("extends")).isFalse();
assertThat(isValidVariableName("package")).isFalse();
assertThat(isValidVariableName("throws")).isFalse();
assertThat(isValidVariableName("implements")).isFalse();
// Primitive types and void
assertThat(isValidVariableName("boolean")).isFalse();
assertThat(isValidVariableName("byte")).isFalse();
assertThat(isValidVariableName("char")).isFalse();
assertThat(isValidVariableName("short")).isFalse();
assertThat(isValidVariableName("int")).isFalse();
assertThat(isValidVariableName("long")).isFalse();
assertThat(isValidVariableName("float")).isFalse();
assertThat(isValidVariableName("double")).isFalse();
assertThat(isValidVariableName("void")).isFalse();
// Control flow
assertThat(isValidVariableName("if")).isFalse();
assertThat(isValidVariableName("else")).isFalse();
assertThat(isValidVariableName("try")).isFalse();
assertThat(isValidVariableName("catch")).isFalse();
assertThat(isValidVariableName("finally")).isFalse();
assertThat(isValidVariableName("do")).isFalse();
assertThat(isValidVariableName("while")).isFalse();
assertThat(isValidVariableName("for")).isFalse();
assertThat(isValidVariableName("continue")).isFalse();
assertThat(isValidVariableName("switch")).isFalse();
assertThat(isValidVariableName("case")).isFalse();
assertThat(isValidVariableName("default")).isFalse();
assertThat(isValidVariableName("break")).isFalse();
assertThat(isValidVariableName("throw")).isFalse();
assertThat(isValidVariableName("return")).isFalse();
// Other keywords
assertThat(isValidVariableName("this")).isFalse();
assertThat(isValidVariableName("new")).isFalse();
assertThat(isValidVariableName("super")).isFalse();
assertThat(isValidVariableName("import")).isFalse();
assertThat(isValidVariableName("instanceof")).isFalse();
// Reserved keywords
assertThat(isValidVariableName("goto")).isFalse();
assertThat(isValidVariableName("const")).isFalse();
}
@Test
void literalsCannotBeUsedAsVariableNames() {
assertThat(isValidVariableName("null")).isFalse();
assertThat(isValidVariableName("true")).isFalse();
assertThat(isValidVariableName("false")).isFalse();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With