views:

140

answers:

2

java.util.Locale is one of those classes where I wonder whether I'm too stupid or the guy who wrote it. Is Mark Davis around?

As far as I can see, this class isn't supposed to be used. The internal cache in the class is private. The factory package private. equals() uses == to compare strings. This means that I can't compare instances of the class for equality unless I create instances myself, put them into a cache somewhere, violating DRY.

Is this what I should do? Is there a sane explanation for this behavior???

+3  A: 

This happens because all Strings passed to the constructors are intern()-ed. A questionable practice, but the behaviour is correct in the end.


The 3-argument constructor is

public Locale(String language, String country, String variant) {
    this.language = convertOldISOCodes(language);
    this.country = toUpperCase(country).intern();
    this.variant = variant.intern();
}

and then later on

private String convertOldISOCodes(String language) { 
    // we accept both the old and the new ISO codes for the languages whose ISO 
    // codes have changed, but we always store the OLD code, for backward compatibility 
    language = toLowerCase(language).intern(); 
Robert Munteanu
You're right, I missed that. Unfortunately, equals() still returns false for my test cases.
Aaron Digulla
Any specific values which fail? I have done a simple test and it passes for me ( just using `new Locale("en","EN")` ).
Robert Munteanu
Okay, found it: I'm reading the Locales back from Db4o which means they get serialized which breaks the class. Wonderful...
Aaron Digulla
`readResolve` looks like it's doing the right thing. Hang on...
Robert Munteanu
I've done a small serialisation test which works just fine. See http://gist.github.com/264403 for the actual code.
Robert Munteanu
Shouldn't you use java.util.Locale.getInstance(String, String, String) anyway?
mhaller
@mhaller: That method is package private (-> no one outside java.util can call it) :(
Aaron Digulla
@Robert: I guess Db4o does something more tricky. I've used a Translator and now it behaves :)
Aaron Digulla
@Aaron : glad to hear you sorted it out.
Robert Munteanu
+1  A: 

You can always use locale.toString() for putting in maps in order to work this around.

Or you can wrap your Locale (class LocaleWrapper { private Locale locale; .. }), implement the equals method properly and then use the wrapper.

Bozho