2

Here is minimal demo case:

class T extends HashMap {
  @Override
  String toString() {
    return "foo"
  }
}

def t = new T()
println t.toString()
println "${t}"​

The output is

foo
[:]

So the @Override toString() never executed for obtaining value within GString. Should I put breakpoint inside it never stopped there. Am I missing something or is it a bug? Other than HashMap and Collection seems to be working fine.

Dee
  • 131
  • 1
  • 10

1 Answers1

4

GString does not call Map.toString() directly and instead it calls

InvokeHelper.write(Writer out, Object object)

Put breakpoint here and see what happens:

I've recorded a short video from debugging session to show you exactly what happens when this object is put inside a GString:

https://youtu.be/HYHdfmwd-lw

I hope it helps.

Szymon Stepniak
  • 40,216
  • 10
  • 104
  • 131
  • Thanks for detailed explanation, this is clear now what happening. Though I am still consider question unanswered because to me it is unclear is it expected behaviour (and if so - why) or is it sounds like a Groovy bug. I am surprised `instanceof Map` give me `true` as for subclassed in Java you always had to use `isAssignableFrom()` for it. I am surprised I am no longer able to threat `Map` as an object in Groovy and customize it's behaviour with inheritance as I need. Anyway is there a workaround available? – Dee Jul 29 '17 at 22:44
  • I think I found a workaround by using `println "${(String) t}"​` which will force it to cast to String which will call `toString()` even before `GString` will start rendering. – Dee Jul 29 '17 at 23:47
  • 1
    You can call `t.toString()` directly without casting. It doesn't change the order of rendering inside `GString`, it changes types that `GString` is about to render - now it sees `String` instead of a `Map` so it renders it as is. – Szymon Stepniak Jul 30 '17 at 08:30