I have some code written in Java that uses Generics. This is a simple version:
// In Java
public interface Testable {
void test();
}
public class TestableImpl implements Testable {
@Override
public void test(){
System.out.println("hello");
}
}
public class Test {
public <T extends Testable> void runTest(Collection<T> ts){
System.out.println("Collection<T>");
for(T t: ts)
t.test();
}
public void runTest(Object o){
System.out.println("Object");
System.out.println(o);
}
}
// in Groovy - this is how I have to use the code
Test test = new Test()
test.runTest([new TestableImpl(), new TestableImpl()])
test.runTest([1,2,3]) //exception here
I am suprised that the second method call is dispatched to the wrong method (wrong in my Javish understanding). Instead calling the Object
overload, the Collection
gets called.
I am using Groovy 2.1.9, Windows 7.
And the exception is:
Caught: org.codehaus.groovy.runtime.typehandling.GroovyCastException:
Cannot cast object '1' with class 'java.lang.Integer' to class 'Testable'
org.codehaus.groovy.runtime.typehandling.GroovyCastException:
Cannot cast object '1' with class 'java.lang.Integer' to class 'Testable'
Why? How to solve this?
How to make Groovy call the same method as Java would?
edit: to further explain the case, I'd like to write a Spock test for it (just imagine the method returns something, say a String..):
def "good dispatch"(in,out) {
expect:
test.runTest(in) == out
where:
in | out
new Object() | "a value for Object"
new Integer(123) | "a value for Object"
[1,2,3] | "a value for Object"
[new TestableImpl()] | "a value for Testable Collection"
}
Others have suggested possible ways to solve your problem but here is WHY it happens.
Groovy - being a dynamic language - uses the runtime type information to invoke the correct method. Java, on the other hand, determines which method will be used based on the static type.
A simple example that demonstrates the differences between JAVA and GROOVY:
void foo(Collection coll) {
System.out.println("coll")
}
void foo(Object obj) {
System.out.println("obj")
}
In GROOVY:
Object x = [1,2,3] //dynamic type at invocation time will be ArrayList
foo(x)
//OUT: coll
In JAVA:
Object x = Arrays.asList(1,2,3);
foo(x);
//OUT: obj
Collection x = Arrays.asList(1,2,3);
foo(x);
//OUT: coll
Now in your example (it does not really have anything to do with the use of generics):
test.runTest([new TestableImpl(), ...]) //ArrayList --> collection method will be used
test.runTest([1,2,3]) //also ArrayList --> best match is the collection variant
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With