sunnyrjuneja
sunnyrjuneja

Reputation: 6123

Adding A Method to the Global Name Space

I've written a method that I can use ClassName.methodName(args).

How can I make it so I can use methodName(args).

I tried monkey patching Object like so:

class Object {
    def methodName(args) {
        // method definition
    }
}

Update:

I tried what dmahapatro said.

import static groovy.json.JsonOutput.*

Object.metaClass.outputJson = {
    return println(prettyPrint(toJson(it)))
}

outputJson([:])

Return:

Caught: groovy.lang.MissingMethodException: No signature of method: Object.outputJson() is applicable for argument types: (java.util.LinkedHashMap) values: [[:]]
Possible solutions: outputJson(), outputJson(java.lang.Object)
groovy.lang.MissingMethodException: No signature of method: Object.outputJson() is applicable for argument types: (java.util.LinkedHashMap) values: [[:]]
Possible solutions: outputJson(), outputJson(java.lang.Object)
    at Object.run(Object.groovy:7)
[Finished in 2.1s]

The issue created by the edit was because Object.groovy conflicted with Groovy's Object.java. Once I renamed it to ObjectMeta (or any other non conflicting name, it worked).

Upvotes: 1

Views: 157

Answers (2)

dmahapatro
dmahapatro

Reputation: 50275

Using ExpandoMetaClass on Object

Object.metaClass.printsHello = {
    return it
}

assert "Hello" == printsHello("Hello")
assert "Hello" == 'ABC'.printsHello("Hello")
assert "Hello" == 123.printsHello("Hello")
assert "Hello" == new Object().printsHello("Hello")

class A{
    Integer a
}

assert "Hello" == new A(a: 10).printsHello("Hello")

This can also be achieved by using @Category as below

@Category(Object) class CustomizedObject{
    def printsHello(String str){
        return str
    }
}

String.mixin CustomizedObject
assert 'Hello' == 'ABC'.printsHello('Hello')

Integer.mixin CustomizedObject
assert 'Hello' == 123.printsHello('Hello')

BigInteger.mixin CustomizedObject
assert 'Hello' == 123G.printsHello('Hello')

@Mixin(CustomizedObject) //Compile Time Mixin
class A{
}

assert 'Hello' == new A().printsHello('Hello')

If you want to distribute the @Category in a jar, then include CustomizedObject in that jar and use it wherever needed.

import static groovy.json.JsonOutput.*

Object.metaClass.outputJson = {
    return prettyPrint(toJson(it))
}

println outputJson([a: 1, b: 2, c: 3])

//Prints:
{
    "a": 1,
    "b": 2,
    "c": 3
}

Note:-
One thing to catch here is, we are using metaClass on Object directly which can be pivotal sometimes, you should clear the metaClass from object once you are done with it.

Upvotes: 1

Andrew Eisenberg
Andrew Eisenberg

Reputation: 28757

There are several possibilities that you can do. The simplest one is to use categories. In your main method or script do something like this:

use(ObjectExtender) {
  startProgram();
}

And then create the ObjectExtender class like this:

class ObjectExtender {
  static def methodName(Object self, Map args) {
    ...
  }
}

As long as you are inside the control flow of the use call, you will be able to call methodName on any object. There are other possibilities, like creating a new metaClass for object, but I'd probably go with categories.

Upvotes: 0

Related Questions