Заводной: утверждение не удалось для карты?

#groovy #assert

Вопрос:

Отказ от ответственности: Я новичок в Groovy.

Я знаю об этом:

 String foo = "foo"
assert foo == "foo"     // true
assert "$foo" == "foo"  // true
 

Но сейчас я работаю над приведенным ниже кодом:

 String a="a"
Map<String, Object> c = ["$a": [b: 'b']] // [a:[b:b]]
assert c == ["$a":[b:'b']] // true
assert c == ["a":[b:'b']]  // false

Caught: Assertion failed: 

assert c == ["a":[b:'b']]
       | |
       | false
       [a:['b':'b']]

Assertion failed: 

assert c == ["a":[b:'b']]
       | |
       | false
       [a:['b':'b']]

    at 1.run(1.groovy:4)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 

Я заблудился. Почему второе утверждение ложно?

Комментарии:

1. можете ли вы показать полный сбой утверждения с помощью трассировки стека

2. конечно, готово. Спасибо

Ответ №1:

Карты не используют равенство для своего набора ключей, но они используют хэш-код. Поэтому, хотя результат в ваших выходных данных выглядит одинаково (например .toString() , используется) и выглядит так, как будто он должен работать, на самом деле это невозможно.

 def x = "x"
assert x=="$x" // works due to equals
assert x.hashCode() == "$x".hashCode()
// fails:
// Caught: Assertion failed: 
// 
// assert x.hashCode() == "$x".hashCode()
//        | |          |    |  |
//        | 120        false|  157
//        'x'               'x'
 

Поэтому вам придется использовать [("$a".toString()): ...] здесь довольно многословное
(что предполагает, что вы на самом деле используете здесь не просто значение a
(которое было бы проще записать как [(a): ...] ), а что-то
более сложное).

Эмпирическое правило: всегда используйте неизменяемые данные в качестве ключей для ваших карт.

Ответ №2:

ответ cfrick содержит правильный обходной путь, но основная причина заключается в том, что я невольно сравнивал строку G со строкой:

 // String
def a="a" // notice def, to let the compiler decide. NOTE: GString a="a" will fail since Groovy won't cast it
assert a.getClass() == java.lang.String // because that's the default when there are no variables

// GString
def b="$a" // quoted variables create GStrings, not Strings. Here, you could also write Gstring b="$a"
assert b.getClass() == org.codehaus.groovy.runtime.GStringImpl // because of the interpolation
assert "$b".toString().getClass() == java.lang.String          // because you converted it

// String
String c="$a"
assert c.getClass() == java.lang.String // because you set the data type to String

// String
def d=(a) // not a, but (a), an expression
assert d.getClass() == java.lang.String // because a is String
assert d.getClass() != org.codehaus.groovy.runtime.GStringImpl

// String
String e="${ -> a }" // with zero arguments, the entire closure result is returned
assert e.getClass() == java.lang.String // lazy evaluation - because you set the data type to String

// Map (verbose)
Map<String, Object> f = [("$b".toString()): [c: 'c']] // to use a var as a key, you need an expression, thus ()
assert f == [("$b".toString()): [c: 'c']] // because you force b from GString to String
assert f != [("$b"): [c: 'c']] // because b is Gstring, not String

// Map (less verbose)
Map<String, Object> g = [ (b.toString()): [c: 'c']] // to use a var as a key, you need an expression, thus ()
assert g == [ (b.toString()): [c: 'c']] // because you force b from GString to String
assert g != [("$b"): [c: 'c']] // because b is Gstring, not String