Groovy铸造集合没有任何用途

Par*_*bay 9 java generics groovy casting spock

我有一些使用Generics编写的Java代码.这是一个简单的版本:

// 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
Run Code Online (Sandbox Code Playgroud)

我很惊讶第二个方法调用被调度到错误的方法(在我的Javish理解中是错误的).而是调用Object重载,调用Collectiongets.

我使用的是Groovy 2.1.9,Windows 7.

例外是:

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'
Run Code Online (Sandbox Code Playgroud)

为什么?怎么解决这个?

如何使Groovy调用与Java相同的方法?


编辑:为了进一步解释这个案例,我想为它编写一个Spock测试(想象一下该方法返回一些东西,比如说一个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"

}
Run Code Online (Sandbox Code Playgroud)

kme*_*era 8

其他人提出了解决问题的可能方法,但这就是为什么会发生这种情况.

Groovy - 作为动态语言 - 使用运行时类型信息来调用正确的方法.另一方面,Java根据静态类型确定将使用哪种方法.

一个简单的例子,演示了JAVA和GROOVY之间的区别:

void foo(Collection coll) {
    System.out.println("coll")   
}

void foo(Object obj) {
    System.out.println("obj")   
}
Run Code Online (Sandbox Code Playgroud)

GROOVY中:

Object x = [1,2,3] //dynamic type at invocation time will be ArrayList
foo(x)
//OUT: coll
Run Code Online (Sandbox Code Playgroud)

JAVA中:

Object x = Arrays.asList(1,2,3);
foo(x);
//OUT: obj
Collection x = Arrays.asList(1,2,3);
foo(x);
//OUT: coll
Run Code Online (Sandbox Code Playgroud)

现在在你的例子中(它与泛型的使用没有任何关系):

test.runTest([new TestableImpl(), ...]) //ArrayList --> collection method will be used
test.runTest([1,2,3]) //also ArrayList --> best match is the collection variant
Run Code Online (Sandbox Code Playgroud)


Aar*_*lla 1

发生这种情况是因为 Java 在编译时从代码中剥离了通用信息。

当 Groovy 尝试在运行时选择正确的方法时,它会ArrayList为第二次调用获取一个 as 参数(注意:不再有通用信息),该参数runTest(Collection tx)runTest(Object o).

有两种方法可以解决这个问题:

  • 创建两个不同名称的方法
  • 删除runTest(Collection)。而是使用instanceofinrunTest(Object)来确定参数是否是正确类型的集合并委托给新的内部方法runTestsInCollection()