Overload == in Groovy to not return boolean - groovy

I'm currently developing a DSL using Groovy for a math related API written in Java.
The Expression class has a method with the following signature:
public Constraint equals(Expression that)
We want to define a constraint, which will only be evaluated later.
Is it possible to override == using our equals implementation so that it doesn't return boolean but Constraint?

No, as far as I know, it is not possible...
The == operator at some point ends up in DefaultTypeTransformation.java::compareEqual which returns boolean, so even if you do:
class Yay {}
class Woo {
String equals(Yay y) {
'hello'
}
}
println new Woo() == new Yay()
You will get the exception:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean
at org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation.compareEqual(DefaultTypeTransformation.java:641)
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.compareEqual(ScriptBytecodeAdapter.java:684)
at ConsoleScript3.run(ConsoleScript3:9)
It will work with a.equals(b), but not a == b

Related

Creation of an instance with `with` block causes a type issue

I am using Groovy to create a package that I use in ReadyApi.
In a Groovy script test step, I do the following:
class B {
String value
boolean isSomething
}
class A {
String name
B propB
public A() {
this.name = "Maydan"
}
}
def x = (A) new A().with { propB = new B(value: "Abc", isSomething: true) }
And I get the following error:
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'B#6c218cea' with class 'B' to class 'A'
error at line: 15
Does someone know why? It doesn't make any sense to me.
Kind regards.
PS: I would like to create an instance of class A (by using its parameterless constructor) and setting its field propB in a single statement
You need to return your A object from the .with closure. You may do it like that:
def x = (A) new A().with { propB = new B(value: "Abc", isSomething: true); return it}
but to me personally it looks a little bit odd. I would do it like that:
def x = new A(propB: new B(value: "Abc", isSomething: true))
The same effect, but more compact and readable. It doesn't require to change your A and B definitions, this "map constructor" works out of the box in Groovy, it will call your parameterless constructor and then assigns the necessary fields (propB in your case).

Groovy coercing List to Map doesn't throw ClassCastException or what is ArrayList1_groovyProxy?

I was writing code where I tried to cast an Object to a Map.
Map a = object as Map
I could alternatively use
Map a = (Map) object
and the whole question would be irrelevant as this throws a ClassCastException if the object is of type List, but by using the former I encountered an interesting thing.
If the object is a List, i.e. object = [], groovy type coercion will behave different from what I expected.
My expectation was to get a ClassCastException, but instead I got a resulting object. But this object seems odd. It is an instance of List and an instance of Map and using .toString() on it resulted in the output of a List, not a Map ([a,b]). Also it is not possible to set a value on the map with a['c'] = 'c'. This results in a java.lang.IllegalArgumentException: argument type mismatch.
Map a = ['a', 'b'] as Map
println(a)
println(a instanceof List)
println(a instanceof Map)
println(a.getClass())
results in the following output:
[a, b]
true
true
class ArrayList1_groovyProxy
I tried to google to find out what this ArrayList1_groovyProxy is, but couldn't find anything.
It still doesn't make sense to me, that the coercion returns an object that obviously is not really what it should be and also seems kind of broken, instead of just throwing a ClassCastException.
Can anyone explain to me the reasoning behind that behavior instead of throwing the exception and explain the use of ArrayList1_groovyProxy? Or is this just a bug in groovy?
The as operator calls the asType method with the provided type as the argument to the method.
You can see the default implementation for asType in DefaultGroovyMethods.
Since Map is an interface, it will eventually call ProxyGenerator.INSTANCE.instantiateDelegate(interfaces, obj), which returns a dynamic proxy that implements Map.
/**
* Converts a given object to a type. This method is used through
* the "as" operator and is overloadable as any other operator.
*
* #param obj the object to convert
* #param type the goal type
* #return the resulting object
* #since 1.0
*/
#SuppressWarnings("unchecked")
public static <T> T asType(Object obj, Class<T> type) {
if (String.class == type) {
return (T) InvokerHelper.toString(obj);
}
// fall back to cast
try {
return (T) DefaultTypeTransformation.castToType(obj, type);
}
catch (GroovyCastException e) {
MetaClass mc = InvokerHelper.getMetaClass(obj);
if (mc instanceof ExpandoMetaClass) {
ExpandoMetaClass emc = (ExpandoMetaClass) mc;
Object mixedIn = emc.castToMixedType(obj, type);
if (mixedIn != null)
return (T) mixedIn;
}
if (type.isInterface()) {
try {
List<Class> interfaces = new ArrayList<Class>();
interfaces.add(type);
return (T) ProxyGenerator.INSTANCE.instantiateDelegate(interfaces, obj);
} catch (GroovyRuntimeException cause) {
// ignore
}
}
throw e;
}
As to why Groovy goes to such great lengths to coerce types--it's basically the nature of Groovy: making Java coding easier. You can construct an object by passing a Map to its constructor, or even coerce a Map instance to a particular type; so why not let every object be coerced back into a Map via the as operator?

Groovy type definitions

class GroovyClass {
def aVariable
void setAVariable(aVariable)
{
this.aVariable = aVariable;
}
}
My understanding was that we don't need to specify the type of a variable in a groovy class. But Groovy compiler complains if I declare 'aVariable' , why isn't it considered as a typeless variable with default accessibility ? Should every variable be defined with a def in Groovy both local and class ? Why is it that the function definition doesn't have to begin with a def ? and when I'm passing in a variable to the setter, it doesn't need any def in there ?
That code works fine. What do you mean by "Groovy compiler complains"?
You can define that function with a def if you wanted, and it would return aVariable (as that is what the assignment operatop returns), however, it wouldnt be following the standard for Java Beans in that setters should return null
Given that however, I can run:
a = new GroovyClass()
a.aVariable = 3
And it works fine
Edit
Basically, it's all down to the Groovy parser. The parser expects some sort of list of 1..N keywords defining it's type or visibility, and then a name for the variable. So the following are all valid:
class OkA {
def aValue
}
class OkB {
private aValue
}
class OkC {
private String aValue
}
But you cannot just (with the current parser) say:
class BadA {
aValue
}
Thinking about it, there's no reason I can currently think of for this restriction (as you can declare vars without def in Groovy), but the restriction is there, so you need to type def when defining class attributes.

#Delegate class without default constructor

How can I create a delegate class in Groovy for a class which doesn't have a default constructor? I would like to decorate JUnit's ResultPrinter but am getting an error about the missing constructor.
I don't understand your issue. I just tried this with Java's Short — which also does not have a default constructor.
Everything worked as expected, except if you didn't initialize the delegated object, you get an NPE.
Is it possible you are using #Delegate incorrectly? Delegate doesn't decorate existing classes, it allows you to use an existing classes methods in your own class. It's like extend, but without the class inheritance.
Example code:
class Foo {
#Delegate Short num
String bar
String toString() { "$bar: $num" }
}
def f = new Foo(bar: 'bob', num: 34 as Short)
println f // OK
println f.doubleValue() // OK
f = new Foo()
println f.doubleValue() // NPE
(Alternatively, providing some useful information, such as the actual error and stacktrace, and example code, will get you more useful responses.)

How do you override a method for a java type instance with Groovy meta programming?

I am trying to override the functionality of a method of a java type instance in my Groovy code but I am getting a classcast exception.
I looked at the guide posted here but I can not get it to work.
Since my actual problem is a bit of mess, below is some runnable example code that fails with the same error.
In the example I want to override the substring method of an instance of the java.lang.String class. In reality I want to override a method of an instance of a class that does not have a corresponding Groovy implementation, so the answer to my example is not to simply use a Groovy string instance.
class example {
static void main(args) {
java.lang.String hey = new java.lang.String("hey")
ExpandoMetaClass emc = new ExpandoMetaClass( java.lang.String, false )
emc.substring = {
"This is not a very good substring implementation"
}
emc.initialize()
def proxiedHey = new groovy.util.Proxy().wrap(hey)
proxiedHey.setMetaClass(emc)
printf proxiedHey.toString()
printf proxiedHey.substring(1)
}
}
The above example fails at line 12, i.e printf meh.toString(). The exception thrown is
Caught: java.lang.ClassCastException:
groovy.util.Proxy cannot be cast to
java.lang.CharSequence at
example.main(test.groovy:12)
So, any ideas on what I am doing wrong or if there is another way to solve my problem of adding and/or overriding methods of a java type instance?
I am using Groovy version 1.7.4.
You are creating an ExpandoMetaClass for java.lang.String, but assigning it to a groovy.util.Proxy. Make a metaClass for groovy.util.Proxy instread, like so:
java.lang.String hey = new java.lang.String("hey")
def proxiedHey = new groovy.util.Proxy().wrap(hey)
ExpandoMetaClass emc = new ExpandoMetaClass( groovy.util.Proxy, false )
emc.substring = {
"This is not a very good substring implementation"
}
emc.initialize()
proxiedHey.setMetaClass(emc)
printf proxiedHey.toString()
printf proxiedHey.substring(1)
Have you looked at Pimp my Library Pattern which allows you to add using Groovy Categories. You might find it more convenient and easy to understand in your case.
#Category(String)
class StringSubstrCategory {
def substring( int n) {
"This is not a very good substring implementation"
}
}
use (StringSubstrCategory) {
"hey".substring(1)
}

Resources