Forbidden APIs of Java
Dealing with locale settings and APIs can be hazardous. Read on to find out how to deal with this situation!
Join the DZone community and get the full member experience.
Join For FreeSoftware developers try to take care of many cases when they develop software. They make checks for null comparisons, checking for a negative for a function variable which should be positive, etc. They also want to write generic software which can run on Windows, Linux, etc. cause they do not know where it will be run.
However, there may be still huge problems which haven’t been detected. Do you make case insensitive String comparisons in Java? Consider this:
String city = "istanbul";
System.out.println(city.toUpperCase());
You may accept that result will be:
ISTANBUL
On the other hand if you run it at a Turkish locale, the result will be:
İSTANBUL
This may break your really well designed software! When you upper case an ‘i‘ it will be ‘İ‘ and when you lower case an ‘I‘ it will be ‘ı‘ in Turkish. You can see a similar effect for a getBytes() method when you pass different character sets into it.
Problem
Bear in mind that: Do not use default locale, character sets etc.! OK, but how we can detect such cases? There is a plugin for it: forbiddenapis Add it to your project as described at forbiddenapis wiki. When you run it you will probably have such errors:
Forbidden method invocation: java.text.DecimalFormat#(java.lang.String) [Uses default locale]
Forbidden method invocation: java.lang.String#toLowerCase() [Uses default locale]
Forbidden method invocation: java.util.Scanner#(java.io.InputStream) [Uses default charset]
Forbidden class/interface use: java.io.FileReader [Uses default charset]
Forbidden method invocation: java.text.SimpleDateFormat#(java.lang.String) [Uses default locale]
Forbidden method invocation: java.text.MessageFormat#format(java.lang.String,java.lang.Object[]) [Uses default locale]
Solution
As I mentioned, do not use default Locale, character sets, etc. However, which Locale should we use if we won’t use default Locale? Using Locale.ENGLISH may seem a solution for this, but what if English character set changes in the future? We should find a generic solution: using Locale.ROOT. These are examples of fixing such errors:
- return MessageFormat.format("Stopping in {0} seconds.", DELAY_SEC);
+ return new MessageFormat("Stopping in {0} seconds.", Locale.ROOT).format(DELAY_SEC);
- return new FileWriter(seedFile);
+ return new OutputStreamWriter(new FileOutputStream(seedFile), StandardCharsets.UTF_8));
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ROOT);
- value.getBytes();
+ value.getBytes(StandardCharsets.UTF_8);
- new FileReader(arg[0]);
+ new InputStreamReader(new FileInputStream(arg[0]), StandardCharsets.UTF_8));
- strValue.toLowerCase();
+ strValue.toLowerCase(Locale.ROOT);
- DateFormat df = DateFormat.getDateTimeInstance();
+ DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.ROOT);
Conclusion
As a result, you should not trust default Locale and character sets to avoid such problems. You should find and fix such API calls.
Published at DZone with permission of Furkan Kamaci, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments