Tuesday, November 10, 2009

Groovy Goodness: Define Your Own Type Conversion

In Groovy we can override the method asType() to convert an object into another type. We can use the method asType() in our code to invoke the convertion, but we can even make it shorter an use as.

class Size {
    def x, y
    
    Object asType(Class clazz) {
        if (clazz == SquaredSize) {
            new SquaredSize(x: x**2, y: y**2)
        }
    }
}

class SquaredSize {
    def x, y
    
    String toString() { "x: $x, y: $y" }
}

def size = new Size(x: 10, y: 5)
def squared = size as SquaredSize  // Or size.asType(SquaredSize)

println squared  // Output: x: 100, y: 25

assert 100 == squared.x
assert 25 == squared.y

4 comments:

codecraig said...

Ok say I want to call the original "asType" implementation for some reason.

For example:
def origAsType = String.&asType
String.metaClass.asType = {Class klazz ->
if (delegate.contains("miles")) {
...
}
else {
origAsType.call(...) ???
}
}

basically how can I call the original "asType" (origAsType here)?

mrhaki said...

@codecraig: to invoke the original asType you only have to cast the first argument to Object.
String.metaClass.asType = { Class klazz ->
if (delegate.contains("miles")) {
...
} else {
asType((Object) delegate, klazz)
}

codecraig said...

so I have this example:

def origAsType = String.&asType
String.metaClass.asType = {Class klazz ->
if (delegate.contains(".")) {
def octets = delegate.split("\\.")
if (octets.length == 4) {
return 1234L
}
}
return origAsType((Object) delegate, klazz)
}

println "10.1.2.3" as Long
println "49" as Long

The first call (which contains periods in it) works the second one fails with:

No signature of method: java.lang.String.asType() is applicable for argument types: (java.lang.String, java.lang.Class) values: [49, class java.lang.Long]

what am I doing wrong? I should mention, I tried just calling "asType((Object) delegate, klazz)" instead of using "origAsType" but I got the same error.

mrhaki said...

@codecraig: I've found the answer for the problem:

def oldAsType = String.metaClass.getMetaMethod('asType', [java.lang.Class] as Class[])
String.metaClass.asType = { Class klazz ->
if (delegate.contains(".")) {
def octets = delegate.split("\\.")
if (octets.length == 4) {
return 1234L
}
}

return oldAsType.invoke(delegate, klazz)
}


println "10.1.2.3" as Long
println "49" as Long