toUpperCase() and toLowerCase()
Convert case with an explicit Locale, and avoid the Turkish-i and German-sharp-s bugs that the default locale causes.
public String toUpperCase(Locale locale)- Added in Java Java 1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$localeoptional | Locale= Locale.getDefault() | The locale whose case rules apply. The no-argument overloads use Locale.getDefault(). |
Return value
StringA new String with every char converted to upper case using the rules of the given locale. toLowerCase(Locale) is the mirror method.
Errors
NullPointerException if locale is null.
Changelog
- PHP Java 1.1The overloads taking a Locale were added.
toUpperCase() and toLowerCase() return a new String with the case changed. Both have two forms: one that takes a Locale, and one with no arguments that uses the JVM's default locale. In server code, always pass a locale.
The Turkish i
Turkish has a dotted and a dotless i in both cases. Under a Turkish locale, "TITLE".toLowerCase() is "tıtle" with a dotless ı, and "title".toUpperCase() is "TİTLE" with a dotted İ. Code that lowercases a header name, a JSON key or an enum constant then fails to match, but only on servers whose default locale is Turkish (or Azerbaijani). That is the worst kind of bug: it passes every test and breaks in one region.
The fix is Locale.ROOT for anything machine-readable: protocol tokens, identifiers, enum names, file extensions, config keys and lookup keys. Use a real user locale only when the result is shown to a person in their language.
Length can change
Case mapping is not always one char to one char. "straße".toUpperCase(Locale.ROOT) is "STRASSE", one char longer, and lowercasing İ outside Turkish gives i plus a combining dot. Never reuse indexes from the original String on the converted one.
Lowercasing for comparison
To compare two values without regard to case, equalsIgnoreCase avoids creating copies. To use a value as a map key or a database lookup, store a normalised form, typically strip().toLowerCase(Locale.ROOT), and normalise incoming values the same way. Keep the original if you need to show it back to the user.
Like every String method, these do not modify the String. email.toLowerCase(Locale.ROOT); on its own does nothing.
Example
When to use it
- Normalising email addresses before using them as a unique key or login lookup.
- Mapping a status query parameter such as 'active' onto an enum constant.
- Making file extensions and header names case-insensitive in a request handler.
Frequently asked questions
Why should I use toLowerCase(Locale.ROOT)?
The no-argument version uses the JVM's default locale. Under Turkish or Azerbaijani rules, I and i map to dotless or dotted variants, so identifiers and keys stop matching. Locale.ROOT gives the same result on every server.
Does toUpperCase change the original String?
No. Strings are immutable; it returns a new String. Assign the result, for example s = s.toUpperCase(Locale.ROOT).
More examples
Normalising emails as lookup keys
The stored key and the incoming value go through the same normalisation, so the lookup succeeds whatever the user typed. Strictly, the part before @ may be case-sensitive, but almost every provider ignores case and most sign-in systems do the same.
void main() {
Map<String, String> usersByEmail = new HashMap<>();
usersByEmail.put("[email protected]", "user-1");
String login = " [email protected] ";
String key = login.strip().toLowerCase(Locale.ROOT);
IO.println(key);
IO.println(usersByEmail.get(key));
IO.println(usersByEmail.get(login));
}
// Output:
// [email protected]
// user-1
// nullParsing an enum from client input
With Locale.ROOT the value maps to the constant. The Turkish rules produce a dotted capital I, so valueOf fails; that is what the no-argument toUpperCase() does on a server whose default locale is Turkish.
enum Status { ACTIVE, DISABLED }
void main() {
String fromClient = "disabled";
IO.println(Status.valueOf(fromClient.toUpperCase(Locale.ROOT)));
String wrong = fromClient.toUpperCase(Locale.forLanguageTag("tr"));
IO.println(wrong);
try {
Status.valueOf(wrong);
} catch (IllegalArgumentException e) {
IO.println("no enum constant for " + wrong);
}
}
// Output:
// DISABLED
// DİSABLED
// no enum constant for DİSABLEDCase changes can change length
Sharp s uppercases to two letters, and a dotted capital I lowercases to i plus a combining dot outside Turkish. Any index or length worked out on the original String is wrong for the converted one.
void main() {
String s = "Gro\u00DFe";
String upper = s.toUpperCase(Locale.ROOT);
IO.println(s.length() + " -> " + upper.length());
IO.println(upper);
IO.println("\u0130".toLowerCase(Locale.ROOT).length());
}
// Output:
// 5 -> 6
// GROSSE
// 2
Discussion