简体   繁体   中英

Groovy Strings vs Java Strings

I have a string in groovy that I make from other strings, example:

def final PREFIX = "myprefix"
def prefix2 = PREFIX + ".whatever1"

Now suppose I have a HashMap and I want to do a lookup using prefix2 as part of a key:

HashMap<String,String> map = new HashMap<String,String>()
map.put("myprefix.whatever1.value","aaa")

If I do:

def key = "${prefix2}.value"
String result=(map.get(key))

Then result = null , but if I do:

String key="${prefix2}.value"
String result=(map.get(key))

Then result = aaa .

I can understand why this happens, obviously a type inference issue. But I still find it makes me feel icky. Something just doesn't "feel right" about it. Do you know what I mean?

Is this type of thing normal and should be "expected"? Am I asking for too much for Groovy to know that if I created the string using quotes, it should work when being used to look up a value in a Hashmap <String, String> without being defined as a String object reference? Is this a bug or a feature?

There are multiple things that need to be take into consideration why this is happening.

When you create a variable HashMap<String,String> map you would expect that like in java you would only be able to add strings as keys and values to it. This is not the case in groovy as the type arguments will not be taken into consideration which means the following will work:

HashMap<String,String> map = new HashMap<String,String>()
map.put(1, 2)   
assert map.get(1) == 2
assert map.get(1) instance of Integer

Also as you stated, an interpolated string like "${prefix2}.value" is an instance of GString ( GStringImpl to be specific) so the following is true

def key = "${prefix2}.value"
assert key instanceof GString

So, map.get(key) will get invoked with a GString parameter without an error since the String constraint is dropped, which would not be an issue if the GStringImpl would give the same hashcode as the String , but it doesn't

assert key == "myprefix.whatever1.value"
assert key.hashCode() != "myprefix.whatever1.value".hashCode()

This is why the get on the map returns a null

The way to work around this is, as you stated using either toString() or assigning the GString to a String variable ( toString() is called implicitly)

Another way, which I would prefer, is to not mix the usual java style map access with the groovy one. If you use the property notation or key indexed on the map using the GString it will work perfectly:

def final PREFIX = "myprefix"
def prefix2 = PREFIX + ".whatever1"

// def map = [:] // shorter will be a LinkedHashMap
HashMap<String,String> map = [:] as HashMap // if you really need HashMap
map."myprefix.whatever1.value" = "aaa"

def key = "${prefix2}.value"

assert map[key] == "aaa"
assert map."${prefix2}.value" == "aaa"
assert map."$key" == "aaa"

You can call toString() method of GString (interpolated string in groovy), which will return a java.lang.String, so if you want to use such a string to retrieve a value from a HashMap, for example, you would use it like so:

 def prefix = "some.string"
 map.get("${prefix}.value".toString())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM