I know there are similar questions but the answers are not satisfying.
I get an Groovy ambiguous method overload error when calling a method with null as parameter.
e.g.:
class A{
sampleMethod (B bObj){
if(bObj == null) {
handleNullArgumentGracefully()
}
... do some cool stuff ...
}
sampleMethod (C cObj){
... do some other cool stuff ...
}
}
now when i call sampleMethod(null) groovy does not know which method it should invoke. Thats clear but is there a possibility to set one method of these two as default method to handle such null calls? I want to handle this on the callee side and not on the caller side (i do not want to cast something on the caller side)
UPDATE:
i found a solution how it could work but i donĀ“t know why: convert the non default method to a closure property
class app {
static void main(String[] args) {
def a = new A()
a.sampleMethod(new B())
a.sampleMethod(new C())
a.sampleMethod(null)
}
}
class A {
def sampleMethod(B bObj = null) {
if (bObj == null) {
println("handle null")
}
println("1")
}
def sampleMethod = { C cObj ->
println("2")
}
}
class B {
}
class C {
}
the following will fail with Ambiguous method overloading for method A#sampleMethod
class A{
def sampleMethod (Number o=null){
println "num $o"
}
def sampleMethod (String o){
println "str $o"
}
}
new A().sampleMethod(null)
this one will work (Object will be called for null):
class A{
def sampleMethod (Number o=null){
println "num $o"
}
def sampleMethod (String o){
println "str $o"
}
def sampleMethod(Object o){
println "obj $o"
}
}
new A().sampleMethod(null)
but i like this one:
class A{
def _sampleMethod (Number o){
println "num $o"
}
def _sampleMethod (String o){
println "str $o"
}
def sampleMethod(Object o){
if(o==null){
println "null"
return null
}else if(o instanceof Number){
return _sampleMethod ((Number) o)
}else if(o instanceof String){
return _sampleMethod ((String) o)
}
throw new IllegalArgumentException("wrong argument type: ${o.getClass()}")
}
}
new A().sampleMethod(null)
It surprise me!
According to the document of groovy, groovy may use "getProperty" method to get the property of a object. So when I want to change the behavier of getting property on the special object, I use a category class to override the "getProperty" method. However, it does not work.
At last, I found groovy framework use the "get" method in the category class to get property, even if the object is not a map.
My question is that is it a bug or groovy just work like that.
This is the category class.
class DynaBeanExtension {
public static void setProperty(DynaBean bean, String propertyName, def newValue) {
try {
PropertyUtilsBean pu = null;
if (bean instanceof CustomWrapDynaBean) {
pu = bean.propertyUtilsBean;
}
if (pu != null) {
pu.setProperty(bean, propertyName, newValue);
} else {
PropertyUtils.setProperty(bean, propertyName, newValue);
}
} catch (IllegalArgumentException ex) {
bean.propertyMissing(propertyName, newValue);
}
}
public static def getProperty(DynaBean bean, String propertyName) {
try {
PropertyUtilsBean pu = null;
if (bean instanceof CustomWrapDynaBean) {
pu = bean.propertyUtilsBean;
}
if (pu != null) {
return pu.getProperty(bean, propertyName);
} else {
return PropertyUtils.getProperty(bean, propertyName);
}
} catch (IllegalArgumentException ex) {
return bean.propertyMissing(propertyName);
}
}
public static def get(DynaBean bean, String propertyName) {
try {
PropertyUtilsBean pu = null;
if (bean instanceof CustomWrapDynaBean) {
pu = bean.propertyUtilsBean;
}
if (pu != null) {
return pu.getProperty(bean, propertyName);
} else {
return PropertyUtils.getProperty(bean, propertyName);
}
} catch (IllegalArgumentException ex) {
return bean.propertyMissing(propertyName);
}
}
This is the test code:
public static class TestSubClass {
private final int e = 3, f = 4;
private final Map<String, Object> m = new HashMap<>();
public int getE() {
return e;
}
public int getF() {
return f;
}
public Map<String, Object> getM() {
return m;
}
#Override
public String toString() {
return "TestSubClass{" + "e=" + e + ", f=" + f + ", m=" + m + '}';
}
}
public static class TestClass {
private final int a = 1;
private final TestSubClass b = new TestSubClass();
public int getA() {
return a;
}
public TestSubClass getB() {
return b;
}
#Override
public String toString() {
return "TestClass{" + "a=" + a + ", b=" + b + '}';
}
}
Map<String, String> pMap = new HashMap<>();
pMap.put("b.e", "c");
PropertyUtilsBean pu = new PropertyUtilsBean();
pu.setResolver(new ExResolver(pMap));
TestClass testObj = new TestClass();
DynaBean bean = new CustomWrapDynaBean(testObj, pu);
int c = use(DynaBeanExtension) {
bean.c;
}
This is the code of ExResolver:
public class ExResolver implements Resolver {
private static final char NESTED = '.';
private static final char MAPPED_START = '(';
private static final char MAPPED_END = ')';
private static final char INDEXED_START = '[';
private static final char INDEXED_END = ']';
private final Resolver resolver;
private final Map<String, String> pMap;
public ExResolver(Map<String, String> pMap) {
this(new DefaultResolver(), pMap);
}
public ExResolver(Resolver resolver, Map<String, String> pMap) {
this.resolver = resolver;
this.pMap = new HashMap<>(pMap);
}
private String resolveExpr(String expression) {
for (Map.Entry<String, String> entry : pMap.entrySet()) {
if (expression.startsWith(entry.getValue())) {
String to = entry.getValue();
if (expression.length() == entry.getValue().length()) {
return entry.getKey();
} else {
int toTest = expression.codePointAt(to.length());
if (toTest == NESTED || toTest == MAPPED_START || toTest == INDEXED_START) {
return entry.getKey() + expression.substring(to.length(), expression.length());
} else {
return expression;
}
}
}
}
return expression;
}
#Override
public int getIndex(String expression) {
expression = resolveExpr(expression);
return resolver.getIndex(expression);
}
#Override
public String getKey(String expression) {
expression = resolveExpr(expression);
return resolver.getKey(expression);
}
#Override
public String getProperty(String expression) {
expression = resolveExpr(expression);
return resolver.getProperty(expression);
}
#Override
public boolean hasNested(String expression) {
expression = resolveExpr(expression);
return resolver.hasNested(expression);
}
#Override
public boolean isIndexed(String expression) {
expression = resolveExpr(expression);
return resolver.isIndexed(expression);
}
#Override
public boolean isMapped(String expression) {
expression = resolveExpr(expression);
return resolver.isMapped(expression);
}
#Override
public String next(String expression) {
expression = resolveExpr(expression);
return resolver.next(expression);
}
#Override
public String remove(String expression) {
expression = resolveExpr(expression);
return resolver.remove(expression);
}
}
"get" is invoked, not "getProperty"
What's more, in the real situation DynaBeanExtension is compiled with groovy. The construction of bean is compiled with java. Then by using binding, I put it into the test code which is a runtime script executed by java code.
This happens in the compilation itself. Let's look at a simpler example.
class Main {
static void main(def args) {
Foo foo = new Foo()
foo.str = ""
foo.str
}
}
For Groovy classes
class Foo {
String str
}
If you decompile the Main class, you'll see it is
public class Main implements GroovyObject {
public Main() {
Main this;
CallSite[] arrayOfCallSite = $getCallSiteArray();
MetaClass localMetaClass = $getStaticMetaClass();
this.metaClass = localMetaClass;
}
public static void main(String... args) {
CallSite[] arrayOfCallSite = $getCallSiteArray();
Foo foo = (Foo)ScriptBytecodeAdapter.castToType(arrayOfCallSite[0].callConstructor(Foo.class), Foo.class);
String str = "";
ScriptBytecodeAdapter.setGroovyObjectProperty(str, Main.class, foo, (String)"str");
arrayOfCallSite[1].callGroovyObjectGetProperty(foo);
}
}
A .[property] = call gets compiled to a ScriptBytecodeAdapter.setGroovyObjectProperty, that in turn calls the chain MetaClassImpl.setProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [setter]
And a .[property] call gets compiled to a arrayOfCallSite[1].callGroovyObjectGetProperty, that in turn calls the chain
AbstractCallSite.callGroovyObjectGetProperty > GetEffectivePogoPropertySite.getProperty > MethodMetaProperty$GetBeanMethodMetaProperty.getProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [getter]
For Java classes
If you use a Java version of the class being called, like this
public class Foo {
private String str;
public String getStr() {
return str;
}
public void setStr(String str) {
this.str = str;
}
}
The same Main decompiles to
public class Main implements GroovyObject {
public Main() {
Main this;
CallSite[] arrayOfCallSite = $getCallSiteArray();
MetaClass localMetaClass = $getStaticMetaClass();
this.metaClass = localMetaClass;
}
public static void main(String... args) {
CallSite[] arrayOfCallSite = $getCallSiteArray();
Foo foo = (Foo)ScriptBytecodeAdapter.castToType(arrayOfCallSite[0].callConstructor(Foo.class), Foo.class);
String str = "";
ScriptBytecodeAdapter.setProperty(str, null, foo, (String)"str");
arrayOfCallSite[1].callGetProperty(foo);
}
}
A .[property] = call gets compiled to a ScriptBytecodeAdapter.setProperty, that in turn calls the chain [Class].setProperty > InvokerHelper.setProperty -> MetaClassImpl.setProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [setter]
And a .[property] call gets compiled to a arrayOfCallSite[1].callGroovyObjectGetProperty, that in turn calls the chain
AbstractCallSite.callGetProperty > GetEffectivePojoPropertySite.getProperty > MethodMetaProperty$GetBeanMethodMetaProperty.getProperty > MetaMethod.doMethodInvoke > CachedMethod.invoke > java.lang.reflect.Method.invoke > [getter]
To correct your code
As you can see from these dispatch chains, you've overridden the getter correctly (since it happens in the class itself), but if you want to override getProperty or setProperty, you have to do this in metaClass, and not the class itself. The behavior you're seeing is expected. This code demonstrates how to override each
class Foo {
String bar
}
// override using setter in category
#Category(Foo)
class FooCategory {
public String getBar() {
println "in getter"
}
public void setBar(String bar) {
println "in setter"
}
}
use (FooCategory) {
Foo foo = new Foo()
foo.bar = ""
foo.bar
}
// override using metaClass
Foo.metaClass.getProperty { String pname ->
println "in getProperty"
}
Foo.metaClass.setProperty { String pname, Object pValue ->
println "in setProperty"
}
Foo foo = new Foo()
foo.bar = ""
foo.bar
outputs
in setter
in getter
in setProperty
in getProperty
And because the getProperty/setProperty call makes the dispatch (eventually) to the getter/setter, you can prevent the getter/setter from being called at all, like this
class Foo {
String bar
}
Foo.metaClass.getProperty { String pname ->
println "in getProperty"
}
Foo.metaClass.setProperty { String pname, Object pValue ->
println "in setProperty"
}
#Category(Foo)
class FooCategory {
String getBar() {
println "in getter"
}
void setBar(String bar) {
println "in setter"
}
}
use (FooCategory) {
Foo foo = new Foo()
foo.bar = "hi foo1"
foo.bar
}
outputs
in setProperty
in getProperty
Why Groovy skips this if statement?
public static void main(String[] args) {
JsonNode jsonNode = JsonNodeFactory.instance.objectNode()
if (jsonNode) {
println 'It works' //doesn't print it
}
}
Explicit checking for null works well:
public static void main(String[] args) {
JsonNode jsonNode = JsonNodeFactory.instance.objectNode()
if (jsonNode != null) {
println 'It works' //works well
}
}
Imports:
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.node.JsonNodeFactory
import com.fasterxml.jackson.databind.node.ObjectNode
Also if I use breakpoints in Intellij it works.
It depends on Groovy Object#asBoolean.
By default it returns false if object is null.
asBoolean()
Coerce an object instance to a boolean value. An object is coerced to
true if it's not null, to false if it is null.
Groovy tries to cast object to boolean with asBoolean() when there is such check.
This example shows that:
public static void main(String[] args) {
AsBoolFalse asBoolFalse = new AsBoolFalse()
if (asBoolFalse) {
println 'AsBoolFalse doesn\'t work' //doesn't print it
}
AsBoolTrue asBoolTrue = new AsBoolTrue()
if (asBoolTrue) {
println 'AsBoolTrue works' //works well
}
}
static class AsBoolFalse {
public boolean asBoolean() {
return false
}
}
static class AsBoolTrue {
public boolean asBoolean() {
return true
}
}
JsonNode overrides asBoolean() to return false, so we need explicit check for null.
I have a class that implements GroovyInterceptable, and I thought it should make all method invocation to go through the 'invokeMethod'. But I find that this is not the case.
class Test implements GroovyInterceptable{
String name
#Override
Object invokeMethod(String name, Object args) {
def metaMethod = this.metaClass.getMetaMethod(name, args)
return metaMethod.invoke(this, "BAR")
}
public static void main(String[] args) {
Test.metaClass.NEWATTRIBUTE = null
def test = new Test()
test.NEWATTRIBUTE = "MEOW1"
println test.NEWATTRIBUTE // shouldnt this be BAR ?
test.setNEWATTRIBUTE("MEOW2")
println test.NEWATTRIBUTE // prints 'BAR'
// much simpler example ....
test.name = "MOOO"
println test.name // shouldnt this be BAR ?
test.setName("MOOO")
println test.name // prints 'BAR'
}
}
I think I read somewhere, that
test.NEWATTRIBUTE = "MEOW1"
doesnt really access the field directly. Groovy will still call the setter method - and hence the invokeMethod should get invoked, shouldnt it?
Thanks
Alex
When setting a property, Groovy invokes setProperty. Add this method to the class:
void setProperty(String prop, val) {
System.out.println "set property $prop with $val"
}
WillP: Thanks a lot. Here is the complete code:
class Test implements GroovyInterceptable{
String name
#Override
Object invokeMethod(String name, Object args) {
def metaMethod = this.metaClass.getMetaMethod(name, args)
return metaMethod.invoke(this, "BAR")
}
void setProperty(String prop, val) {
getMetaClass().setProperty(this, prop, "BAR2");
}
public static void main(String[] args) {
Test.metaClass.NEWATTRIBUTE = null
def test = new Test()
test.NEWATTRIBUTE = "MEOW1"
println test.NEWATTRIBUTE // prints BAR2
test.setNEWATTRIBUTE("MEOW2")
println test.NEWATTRIBUTE // prints BAR
test.name = "MOOO"
println test.name // prints BAR2
test.setName("MOOO")
println test.name // prints BAR
}
}
as it turns out, setProperty() is called regardless the class implements GroovyInterceptable or not. But invokeMethod() is only called when the class implements GroovyInterceptable
I'm very new to Groovy. I wonder how Closures are implemented in Groovy.
Lets say :
def a = { println "Hello" }
a()
when a() is done, what is actually happening behind the scenes? Which method does a() calls to make the closure executable?
Thanks in advance.
Basically:
your closure is a class with specific name
a() invokes doCall() which invokes doCall(Object it) (implicit it in closures)
acallsite contains method names (2 x println) - and are invoked with appropriate arguments
Here you go:
For this Groovy Script:
def a = { println "Hello"; println "Hello2" }
a()
Closure a looks like this:
class Test$_run_closure1 extends Closure
implements GeneratedClosure
{
public Object doCall(Object it)
{
CallSite acallsite[] = $getCallSiteArray();
acallsite[0].callCurrent(this, "Hello");
return acallsite[1].callCurrent(this, "Hello2");
}
public Object doCall()
{
CallSite acallsite[] = $getCallSiteArray();
return doCall(null);
}
protected MetaClass $getStaticMetaClass()
{
if(getClass() != Test$_run_closure1)
return ScriptBytecodeAdapter.initMetaClass(this);
ClassInfo classinfo = $staticClassInfo;
if(classinfo == null)
$staticClassInfo = classinfo = ClassInfo.getClassInfo(getClass());
return classinfo.getMetaClass();
}
public static void __$swapInit()
{
CallSite acallsite[] = $getCallSiteArray();
$callSiteArray = null;
}
private static void $createCallSiteArray_1(String as[])
{
as[0] = "println";
as[1] = "println";
}
private static CallSiteArray $createCallSiteArray()
{
String as[] = new String[2];
$createCallSiteArray_1(as);
return new CallSiteArray(Test$_run_closure1, as);
}
private static CallSite[] $getCallSiteArray()
{
CallSiteArray callsitearray;
if($callSiteArray == null || (callsitearray = (CallSiteArray)$callSiteArray.get()) == null)
{
callsitearray = $createCallSiteArray();
$callSiteArray = new SoftReference(callsitearray);
}
return callsitearray.array;
}
static Class _mthclass$(String s)
{
try
{
return Class.forName(s);
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
private static ClassInfo $staticClassInfo;
public static transient boolean __$stMC;
private static SoftReference $callSiteArray;
static
{
__$swapInit();
}
public Test$_run_closure1(Object _outerInstance, Object _thisObject)
{
CallSite acallsite[] = $getCallSiteArray();
super(_outerInstance, _thisObject);
}
}
It ends up calling one of the Closure.call methods (in this case the one with no args)
There's more info about this in the documentation