Groovy resolves ambiguous methods alphabetically by parameter class name - groovy

Groovy will resolve ambiguous method calls by choosing the method with a parameter type that is alphabetically first. In the following example the method that takes a parameter of type "A" will be called if Groovy calls MyClass.printIt(null).
If you refactor this code to rename class A to class C you will find that the call with a null myVariable will then resolve to the method that takes a parameter of type "B", the bottom one.
class MyTest {
public static void main(String[] args) {
B myVariable = new B()
new MyClass().printIt(myVariable)
myVariable = null
new MyClass().printIt(myVariable)
}
}
class A {}
class B {}
class MyClass {
void printIt(A variable) {
println "TOP method called"
}
void printIt(B variable) {
println "BOTTOM method called"
}
}
the code as listed above produces the following output
BOTTOM method called
TOP method called it called the top method with a variable of type B
once class A is renamed to class C the output will change to
BOTTOM method called
BOTTOM method called
try as many permutations as you like, the method that takes the class with the lowest alphabetical name is called when the variable passed in is null, even if the variable is typed for the other class.
groovy --version
Groovy Version: 2.0.5 JVM: 1.6.0_33 Vendor: Sun Microsystems Inc. OS: Windows 7
My question is why does Groovy do this, is this by design or is it a bug, is this behavior documented anywhere?
Dave

Can't reproduce this on groovy 2.1.6. Can't reproduce this on groovy 2.0.5 either.
In both use cases output is:
BOTTOM method called
TOP method called
When A is renamed to C output is
BOTTOM method called
TOP method called
Gentoo Linux, JDK 1.7.0_25,

On my groovy (3.0.7), it's even "better":
$ groovy test.groovy
BOTTOM method called
Caught: groovy.lang.GroovyRuntimeException: Ambiguous method overloading for method MyClass#printIt.
Cannot resolve which method to invoke for [null] due to overlapping prototypes between:
[class A]
[class B]
groovy.lang.GroovyRuntimeException: Ambiguous method overloading for method MyClass#printIt.
Cannot resolve which method to invoke for [null] due to overlapping prototypes between:
[class A]
[class B]
at MyTest.main(test.groovy:6)

Related

Nomin automap causes infinite loop

I am using Nomin for mapping tasks. As taken from the documentation of Nomin it should be able to map fields with the same name by itself in case automapping has been activated. When activating it, it causes an infinite loop exception.
I have the following:
mappingFor a: CoinsOnMarketPlace, b: Coin
// automap() // when deactivated it works fine, when activated infinite loop
a.coin.name = b.name
a.coin.rank = b.rank
a.priceUSD = b.priceUSD // Could be automapped
a.priceBTC = b.priceBTC // Could be automapped
...
Exception:
org.nomin.core.NominException: ./net/hemisoft/ccm/repository/coinmarketcap2coin.groovy: Recursive mapping rule a = b causes infinite loop!
One thing worth adding regarding your use case - this Recursive mapping rule a = b causes infinite loop! exception is thrown because you use groovy classes in your mapping rule. Nomin uses ReflectionIntrospector and what's important:
It performs getting/setting properties using accessor methods which are called through the Java reflection mechanism. ReflectionIntrospector uses supplied NamingPolicy instance to determine accessor methods. JbNamingPolicy is used by default, this implementation cerresponds the JavaBeans convention. Its InstanceCreator named ReflectionInstanceCreator instantiates objects using Class.newInstance().
Source: http://nomin.sourceforge.net/introspectors.html
A simple Groovy class like:
class Entity {
String name
String somethingElse
}
gets compiled to a Java class that implements GroovyObject providing following methods:
public interface GroovyObject {
Object invokeMethod(String var1, Object var2);
Object getProperty(String var1);
void setProperty(String var1, Object var2);
MetaClass getMetaClass();
void setMetaClass(MetaClass var1);
}
In this case ReflectionInstanceCreator combined with automap() resolves following mappings:
a.property = b.property
and
a = b
where a = b mapping comes from MetaClass getMetaClass() getter method I suppose, because there is no mapping like a.metaClass = b.metaClass resolved. a.property = b.property gets resolved because of Object getProperty(String var1) method.
Solution
This problem can be solved by specifying explicitly ExplodingIntrospector for your mapping script that:
It performs getting/setting properties using a class field immediately through through the Java reflection mechanism and may be useful in case when domain object don't provide accessors for their properties. Supplied instance creator is ReflectionInstanceCreator.
Source: http://nomin.sourceforge.net/introspectors.html
All you have to do is to add
introspector exploding
right below mappingFor a: ..., b: ... header. For example:
import mypackage.Entity
import mypackage.EntityDto
mappingFor a: Entity, b: EntityDto
introspector exploding
automap()
a.test2 = b.test1
Tested with two Groovy classes, worked like a charm. Hope it helps.

Groovy - MissingPropertyException in Class

I am completely new to Groovy and having some trouble just getting this program running. All I'm trying to do is add all the elements of one list into another, but I can't even access the variable I want in the method.
I looked around here for an answer, but all seemed to be just too specific to a person's problem or too technical for my limited understanding of Groovy. Any help is appreciated.
Using Groovy 2.4.5 in IntelliJ IDEA if that makes a difference. Here is the code I am trying to run:
class ListPractice implements Testable {
def mylist = [4,5,6]
/**
* Adds a set of elements to the mylist variable
*
* #paraelts The elements to be added
*/
def addToList(List elts) {
myList.each{
println "I: $it"
}
}
#Override
void testMe() {
addToList([7,8,9])
}
}
But it throws the following error:
Caught: groovy.lang.MissingPropertyException: No such property: myList
for class: ListPractice
Possible solutions: mylist
groovy.lang.MissingPropertyException: No such property: myList for class: ListPractice
Possible solutions: mylist
at ListPractice.addToList(ListPractice.groovy:14)
at ListPractice$addToList.callCurrent(Unknown Source)
at ListPractice.testMe(ListPractice.groovy:36)
at Testable$testMe.call(Unknown Source)
at RunMe$_main_closure1.doCall(RunMe.groovy:12)
at RunMe.main(RunMe.groovy:11)
Since this is my first time using the language and the structure of the code was done by my teacher, I'm not even sure if #Override is necessary or what it does.
Capitalisation is important
You declared your list as mylist
Then you try to call each on myList
It shows you in the error message

how does the groovyc work?

It seems the groovy also support the compiling mode, using groovyc , If I run the following code with dynamic script calling way, I will get "String" method called.
Why I still got "String" even if I compiled the code using groovyc? The type of variable a is "Object", so I expected after compiling the code, I will get the "Object" function called.
Object a = "123"
def foo(Object a) {
println "Object"
}
def foo(String a) {
println "String"
}
foo(a)
Here is relevant section from groovy-docs
In Groovy, the methods which will be invoked are chosen at runtime.
This is called runtime dispatch or multi-methods. It means that the
method will be chosen based on the types of the arguments at runtime.
In Java, this is the opposite: methods are chosen at compile time,
based on the declared types.
There is a sample code in '2. Multi-methods' section, not copying here.
Finally mandatory link to MrHaki's groovy goodness page on this topic

Groovy Attribute should have type 'java.lang.Integer', but found type 'java.lang.Object'

I have the following JUnit test:
public class JavaTest {
final int value = 2;
#Test
#Repeat(times = value)
public void test() {
fail("Not yet implemented");
}
}
The #Repeat annotation comes from easytest-core, and the exact definition is here.
When I compile this as java source everything builds (and runs) fine. When I compile the exact same thing as groovy source, I get:
Groovy:Attribute 'times' should have type 'java.lang.Integer'; but found type 'java.lang.Object' in #org.easetech.easytest.annotation.Repeat GroovyTest.groovy
After searching the internets, I found a few similar discussions on SO and jira.codehaus, but those deal with String - GString problems, so the solutions do not work for me.
How can I fix this?
Updates:
java.version=1.7.0_76
groovy.version=2.3.7
Think you're bumping into the fact groovyc doesn't treat final variables as inline constants like javac does
I tried changing your int variable like this:
final Integer value = Integer.valueOf(2).intValue()
which prevents the variable from being treated as an inline constant. After that change I get a compile error from the #Repeat annotation:
Expected Integer.valueOf(2).intValue() to be an inline constant
It looks like there's some acknowledgement of the inconsistency here in a Groovy JIRA: https://issues.apache.org/jira/browse/GROOVY-1628
There's also some further discussion here in this SO thread:
Does it make sense to mark variable as final in groovy?
It doesn't look like you're going to be able to get groovy to match the Java behavior for this scenario.

Can I redefine String#length?

I'd like to re-implement a method of a Java class. For example, for "hi".length() to return 4. (How) Can I do that?
I know using SomeClass.metaClass I can get a reference to an existing method and define new (or overriding) method, but I can't seem to be able to do that for existing Java methods.
Using Groovy, you can replace any method (even those of final classes) with your own implementation. Method replacement in Groovy uses the meta-object protocol, not inheritance.
Here's the example you requested, i.e. how to make String.length() always return 4
// Redefine the method
String.metaClass.invokeMethod = { name, args ->
def metaMethod = delegate.metaClass.getMetaMethod(name, args)
def result = metaMethod.invoke(delegate, args)
name == 'length' ? 4 : result
}
// Test it
assert "i_do_not_have_4_chars".length() == 4
Seems like it could be possible by abusing String metaClass. But the attempt I've done so far in groovy console didn't led to the expected result :
def oldLength = String.metaClass.length
String.metaClass.length = { ->
return oldLength+10;
}
println "hi".length()
outputs the sad 2
I think you could take a look at Proxy MetaClass or Delegating metaClass.
If you did redefine it, it would only work in Groovy code. Groovy can't change the way Java code executes.
In Groovy, "hi".length() is roughly equivalent to this Java:
stringMetaClass.invokeMethod("hi","length");
Because Groovy doesn't actually call length directly, metaClass tricks work in Groovy code. But Java doesn't know about MetaClasses, so there is no way to make this work.
Although this question is very old I like to point out another way (at least for newer Groovy versions) .
The length() method in java.lang.String is implemented from java.lang.CharSequence interface. In order to reimplement the method using the String-metaClass you need to "override" the method in the metaClass of the interface first.
CharSequence.metaClass.length = { -> -1}
String.metaClass.length = { -> 4 }
assert "i_do_not_have_4_chars".length() == 4
The solution using String.metaClass.invokeMethod changes the behaviour of all String-methods and is problematic. For instance, simply invoking "asdf".size() leads to an exception on my setup.

Resources