Groovy AST Transformations - How can I figure out the return type of a MethodCallExpression? - groovy

With Groovy AST Transformations, how can I figure out the return type of a MethodCallExpression?
MethodCallExpression.getType() always returns java.lang.Object even if I explicitly define the return type of the method in the method definition.

Due to the dynamic nature of groovy, the AST can't know the return type of a method call expression at compile time. For example:
class Example {
String foo() { "foo" }
}
def e = new Example()
assert e.foo() == "foo"
Looks simple enough. foo returns a string, so the MethodCallExpression for e.foo() should have a type of String, right? But what if foo is changed in the metaClass?
class Example {
String foo() { "foo" }
}
def e = new Example()
if (someRuntimeCondition) {
e.metaClass.foo = { -> 42 }
}
assert e.foo() == "foo" // is foo a String or an Int?
The groovy compiler just doesn't have enough information to make any assumptions about the method call since it could change at runtime, so it has to compile it down to an Object.

Related

How do I store a list/array of classes in a Groovy variable?

I want to store a list/array of Java exception classes (e.g. NullPointerException, some custom exception classes, etc.) into a Groovy list/array, but I keep running into either casting problems or MissingPropertyException:
class Foo {
Exception[] foo
List bar
def setFoo(Exception[] values) {
this.foo = values
}
def setBar(List values) {
this.bar = values
}
}
f = Foo()
f.setFoo([NullPointerException.class])
f.setBar(Arrays.asList(NullPointerException.class))
But I can't seem to get it right. Any pointer is much appreciated. Thanks!
Changes to
class Foo {
Class[] foo
List bar
def setFoo(Class[] values) {
this.foo = values
}
def setBar(List values) {
this.bar = values
}
}
def f = new Foo()
f.setFoo([NullPointerException.class] as Class[])
f.setBar(Arrays.asList(NullPointerException.class))
foo should be an array of Class rather than Exception. (Exception is the instance of Exception Class)
You need to explicitly cast list to Class array, which does the conversion from list to array for you.
It can be done with just def list = [UserClass1, UserClass2]. In groovy, even .class not required.
You code should as below, isn't this simple?
class Foo {
def bar
}
def f = new Foo(bar: [NullPointerException, RuntimeException])
assert f.bar instanceof List<Class>
assert (f.bar as Class[]) instanceof Class[]
You can quickly try it online demo

Using a Closure as an argument to a superclass constructor

I seem unable to use a Closure as a parameter to a superclass constructor when it is specified inline.
class Base {
def c
Base(c) {
this.c = c
}
void callMyClosure() {
c()
}
}
class Upper extends Base {
Upper() {
super( { println 'called' } )
}
}
u = new Upper()
u.callMyClosure()
The compilation fails with the message Constructor call must be the first statement in a constructor..
I realize this a somewhat strange use-case, and I can design around it for the time being. But I'm interested, is this is to be expected? Or have I got the syntax incorrect?
I think that the problem is related to the fact that Groovy turns the constructor into something different while trying to compile it as a Java class. Maybe the closure definition is expanded before the call to super generating that error.
A workaround is to define the closure outside the constructor itself:
class Base {
def c
Base(c) {this.c = c}
void callMyClosure() {
c()
}
}
class Upper extends Base {
static cc = {println 'called'}
Upper() {
super(cc)
}
}
u = new Upper()
u.callMyClosure()
It's not so nice but at least it works.. another way could be to define the closure by using a normal new Closure(...) syntax
It might be confusing a closure and a block...can you try
super( { -> println 'called' } )

Method overloading in groovy

I am trying to take advantage of the convenience of groovy's scripting syntax to assign properties, but having trouble with a specific case. I must be missing something simple here. I define class A, B, C as so:
class A {
A() {
println "Constructed class A!"
}
}
class B {
B() {
println "Constructed class B!"
}
}
class C {
private member
C() {
println "Constructed class C!"
}
def setMember(A a) {
println "Called setMember(A)!"
member = a
}
def setMember(B b) {
println "Called setMember(B)!"
member = b
}
}
And then try the following calls in a script:
c = new C()
c.setMember(new A()) // works
c.member = new A() // works
c.setMember(new B()) // works
c.member = new B() // doesn't work!
The last assignment results in an error: 'Cannot cast object of class B to class A". Why doesn't it call the proper setMember method for class B like it does for class A?
The shortcut of using the dot notation for calling a property's setter method doesn't do type checking. Instead it seems to use the first entry in the list of methods with a given name and invoke it.
You can also read Pyrasun's extended comments on the shortcomings of Groovy's property handling.
If you want to bypass this (mis)behavior you have to call the setter directly, as Groovy supports type checking for method calls. Alternatively you could also access the field directly without a setter using
c.#member = new B()
or you do the type checking on your own in a single setter method:
def setMember(def param) {
if (param instanceof A) println "Called setMember(A)!"
if (param instanceof B) println "Called setMember(B)!"
member = param
}

replacing toString using Groovy metaprogramming

In the following Groovy snippet, I attempt to replace both the hashCode and toString methods
String.metaClass.toString = {-> "override" }
String.metaClass.hashCode = {-> 22 }
But when I test it out, only the replacement of hashCode works
String s = "foo"
println s.hashCode() // prints 22
println s.toString() // prints "foo"
Is toString somehow a special case (possibly for security reasons)?
See the first comment on this issue. It says about String's toString and other String related classes:
(...) seems to be intent, it is probably a
good idea to have a faster invocation
for classes that don't allow
overriding toString().
This is a know defect.
Basically Groovy does not correctly override methods that are part of an interface implementation.
This works:
class T {
def doIt() { true }
}
def t = new T()
assert t.doIt()
t.metaClass.doIt = { -> false }
assert !t.doIt()
This doesn't:
interface I {
def doIt()
}
class T implements I {
def doIt() { true }
}
def t = new T()
assert t.doIt()
t.metaClass.doIt = { -> false }
assert !t.doIt()
Because toString() in String comes from CharSequence the correct way to override would be:
CharSequence.metaClass.toString = {-> "silly"}
println "hello world".toString()

In groovy, is there a way to check if an object has a given method?

Assuming that I have an object someObj of indeterminate type, I'd like to do something like:
def value = someObj.someMethod()
Where there's no guarantee that 'someObj' implements the someMethod() method, and if it doesn't, just return null.
Is there anything like that in Groovy, or do I need to wrap that in an if-statement with an instanceof check?
Use respondsTo
class Foo {
String prop
def bar() { "bar" }
def bar(String name) { "bar $name" }
}
def f = new Foo()
// Does f have a no-arg bar method
if (f.metaClass.respondsTo(f, "bar")) {
// do stuff
}
// Does f have a bar method that takes a String param
if (f.metaClass.respondsTo(f, "bar", String)) {
// do stuff
}
Just implement methodMissing in your class:
class Foo {
def methodMissing(String name, args) { return null; }
}
And then, every time you try to invoke a method that doesn't exist, you will get a null value.
def foo = new Foo();
assert foo.someMethod(), null
For more information, take a look here: http://groovy.codehaus.org/Using+methodMissing+and+propertyMissing
You should be able to do something like:
SomeObj.metaClass.getMetaMethod("someMethod")
Or you can fall back to the good old Java reflection API.
You can achieve this by using getMetaMethod together with the safe navigation operator ?.:
def str = "foo"
def num = 42
def methodName = "length"
def args = [] as Object[]
assert 3 == str.metaClass.getMetaMethod(methodName, args)?.invoke(str, args);
assert null == num.metaClass.getMetaMethod(methodName, args)?.invoke(num, args);
if class :
MyClass.metaClass.methods*.name.any{it=='myMethod'}//true if exist
if object :
myObj.class.metaClass.methods*.name.any{it=='myMethod'}//true if exist
In very concise way you can use this:
if(someObj.&methodName){
//it means someObj has the method
}

Resources