Grails how to change a metaclass Method

47 Views Asked by At

I'm trying to remove the square brackets that appear in toString off all collections in Grails. These square brackets are added in the toString of the java.util.AbstractCollection class. So the goal is to replace this toString method and I'm trying to use the ExpandoMetaClass.

I tried to use this code in the Bootstrap file:

 def init = { servletContext ->
     ExpandoMetaClass.enableGlobally()
     AbstractCollection.metaClass.toString << {-> "Test result" }
}

But the error occurs:

Caused by: groovy.lang.GroovyRuntimeException: Cannot add new method [toString] for arguments [[]]. It already exists!

I also tried with the = operator instead of <<. The error does not occur but it has no effect.

Does anybody know how to solve this?

Update:

I realized that the problem is not in the metaclass assignment but probably something that grails does in the entity lists.

Example:

 class SampleObject {
    static hasMany = [sampleList: SampleList]
 }

 AbstractCollection.metaClass.toString = {
     'Groovy'
 }

 println(sampleObject.sampleList.toString()) // print [item1, item2, ...]
 println([].toString()) // prints Groovy

I also tested these:

    AbstractPersistentCollection.metaClass.toString = {
        'Groovy1'
    }

    Collection.metaClass.toString = {
        'Groovy2'
    }

    PersistentSet.metaClass.toString = {
        'Groovy3'
    }
1

There are 1 best solutions below

1
On

You can not use leftShift (<<) operator for the existing methods. You should use equals (=) operator instead, as you're RE-defining the method. Look at this article. I quote:

In Groovy we can add new methods to classes with ExpandoMetaClass using the leftShift (<<) operator. If the method already exists we get an error. To override an existing method we can use the equal operator (=). But we can also use the equal operator (=) to add a new method, but then we don't get an error when the method already exists.

try {
    Integer.metaClass.toString << { 'Groovy' } // Cannot use << for existing method.
    assert false
} catch (e) {
    assert 'Cannot add new method [toString] for arguments [[]]. It already exists!' == e.message
}
 
Integer.metaClass.toString = { 'Groovy' }
assert 'Groovy' == 100.toString()

I hope it will help you.