Groovy中字符串插值时重写HashMap.toString()失效的原因与解决
问题原因
Groovy的字符串插值("${xxx}")和as String类型转换,底层逻辑并不是直接调用对象的toString()方法。对于实现了Map接口的对象,Groovy内部有专门的字符串序列化逻辑,会直接调用AbstractMap(HashMap的父类)的原生toString()实现,完全绕过子类重写的方法。而直接调用.toString()时,遵循Java方法重写规则,所以会执行你自定义的逻辑。
解决办法
这里提供几个可行方案:
方案1:用组合替代继承(推荐)
不要直接继承HashMap,而是内部持有一个HashMap实例并实现Map接口。这样Groovy不会把它识别为原生Map,字符串插值和类型转换时会正常调用你重写的toString():
class Foo implements Map { private Map delegate = new HashMap() String toString() { "foo" } // 实现Map接口的所有方法,直接委托给内部的delegate @Override int size() { delegate.size() } @Override boolean isEmpty() { delegate.isEmpty() } @Override boolean containsKey(Object key) { delegate.containsKey(key) } @Override boolean containsValue(Object value) { delegate.containsValue(value) } @Override Object get(Object key) { delegate.get(key) } @Override Object put(Object key, Object value) { delegate.put(key, value) } @Override Object remove(Object key) { delegate.remove(key) } @Override void putAll(Map m) { delegate.putAll(m) } @Override void clear() { delegate.clear() } @Override Set keySet() { delegate.keySet() } @Override Collection values() { delegate.values() } @Override Set<Map.Entry> entrySet() { delegate.entrySet() } }
方案2:重写asType方法
通过重写asType方法,指定转换为String类型时返回自定义内容,同时覆盖插值场景的逻辑:
class Foo extends HashMap { String toString() { "foo" } @Override Object asType(Class clazz) { if (clazz == String) { return toString() } super.asType(clazz) } }
方案3:显式调用toString(临时 workaround)
如果不想修改类结构,在字符串插值时显式调用.toString(),强制触发自定义逻辑:
def test = new Foo() test.put("abc", 123) println "${test.toString()}" // 输出"foo"
内容的提问来源于stack exchange,提问作者Xophmeister
相关产品推荐
相关产品推荐

