在Groovy中List和List <string>是否相同?</string>

时间:2014-02-24 10:00:46

标签: list arraylist groovy

问题1

在Groovy中是否使用List(对象列表)或List<String>(字符串列表)是否无关紧要?

在下面的代码示例中,两个列表最终都是ArrayList(对象的ArrayList)。本来希望第二个列表是ArrayList<String>(字符串的数组列表)。

Groovy在编译类时是否会丢失类型信息,并在执行编译的类时推断它?

示例1

List untypedList = ["a", "b", "c"]
List<String> typedList = ["a", "b", "c"]

println "Untyped list List:       ${untypedList.getClass()}"
println "Typed list List<String>: ${typedList.getClass()}"

输出1

Untyped list List:       class java.util.ArrayList
Typed list List<String>: class java.util.ArrayList // Would have expected ArrayList<String>

问题2

我原本期望下面示例中的行typedList << new Integer(1)失败并出现异常,因为我试图将int放入字符串列表中。任何人都可以解释为什么我可以int添加String - 键入List吗?

输出显示它仍为Integer,即它不会即时转换为String“1”。

示例2

List untypedList = ["a", "b", "c"]
List<String> typedList = ["a", "b", "c"]

untypedList << new Integer(1)
typedList << new Integer(1) // Why does this work? Shouldn't an exception be thrown?

println "Types:"
println "Untyped list List:       ${untypedList.getClass()}"
println "Typed list List<String>: ${typedList.getClass()}"

println "List contents:"
println untypedList
println typedList

println "Untyped list:"
untypedList.each { println it.getClass() }
println "Typed list:"
typedList.each { println it.getClass() }

输出2

Types:
Untyped list List:       class java.util.ArrayList
Typed list List<String>: class java.util.ArrayList
List contents:
[a, b, c, 1]
[a, b, c, 1]
Untyped list:
class java.lang.String
class java.lang.String
class java.lang.String
class java.lang.Integer
Typed list:
class java.lang.String
class java.lang.String
class java.lang.String
class java.lang.Integer

3 个答案:

答案 0 :(得分:24)

在运行Groovy “normal”时,泛型会在编译之前被丢弃,因此只在源代码中存在,作为对开发人员的有用提醒。

但是,您可以使用@CompileStatic@TypeChecked使Groovy尊重这些泛型并在编译时检查事物的类型。

作为一个例子,考虑我有以下项目结构:

project
 |---- src
 |      |---- main
 |             |---- groovy
 |                    |---- test
 |                           |---- ListDelegate.groovy
 |                           |---- Main.groovy
 |---- build.gradle

使用代码:

<强>的build.gradle

apply plugin: 'groovy'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.2.1'
}

task( runSimple, dependsOn:'classes', type:JavaExec ) {
    main = 'test.Main'
    classpath = sourceSets.main.runtimeClasspath
}

<强> ListDelegate.groovy

package test

class ListDelegate<T> {
    @Delegate List<T> numbers = []
}

<强> Main.groovy

package test

class Main {
    static main( args ) {
        def del = new ListDelegate<Integer>()
        del << 1
        del << 'tim'
        println del
    }
}

现在,运行gradle runSimple为我们提供输出:

:compileJava UP-TO-DATE
:compileGroovy
:processResources UP-TO-DATE
:classes
:runSimple
[1, tim]

BUILD SUCCESSFUL

Total time: 6.644 secs

正如您所看到的那样,泛型被丢弃了,它只是将IntegersStrings添加到List的{​​{1}} Integers只有ListDelegate.groovy

现在,如果我们将package test import groovy.transform.* @CompileStatic class ListDelegate<T> { @Delegate List<T> numbers = [] } 更改为:

:compileJava UP-TO-DATE
:compileGroovy
:processResources UP-TO-DATE
:classes
:runSimple
[1, tim]

BUILD SUCCESSFUL

Total time: 6.868 secs

然后又跑了:

ListDelegate

我们得到相同的输出!!这是因为虽然Main现在已经静态编译,但我们的ListDelegate类仍然是动态的,因此在构建Main.groovy之前仍然会抛弃泛型...所以我们也可以更改package test import groovy.transform.* @CompileStatic class Main { static main( args ) { def del = new ListDelegate<Integer>() del << 1 del << 'tim' println del } } 到:

gradle runSimple

现在重新开始:compileJava UP-TO-DATE :compileGroovy startup failed: /Users/tyates/Code/Groovy/generics/src/main/groovy/test/Main.groovy: 10: [Static type checking] - Cannot find matching method test.ListDelegate#leftShift(java.lang.String). Please check if the declared type is right and if the method exists. @ line 10, column 9. del << 'tim' ^ 1 error :compileGroovy FAILED 给我们:

String

正如您所期望的那样,未能在我们声明的Integer列表中添加CompileStatic

事实上,你只需要Main.groovy {{1}}类,这个错误就会被提起,但我总是喜欢尽我所能,而不仅仅是我需要的地方。

答案 1 :(得分:4)

正如@tim_yates所说,可以使用@TypeChecked / @CompileStatic注释启用编译时检查。

另一种方法是通过使用Collections.checkedList()包装集合来启用运行时类型检查。虽然这不使用泛型或声明的类型,但在运行时强制执行它有时更适合松散类型的动态代码。这是一个不特定于groovy的Java平台功能。

示例:

// no type checking:
list1 = ["a", "b", "c"]
list1 << 1
assert list1 == ["a", "b", "c", 1]
// type checking
list2 = Collections.checkedList(["a", "b", "c"], String)
list2 << 1
// ERROR java.lang.ClassCastException:
// Attempt to insert class java.lang.Integer element into collection with element type class java.lang.String

答案 2 :(得分:3)

来自Wikipedia,对于Java:

  

在编译时检查泛型的类型正确性。通用的   然后在名为type erasure的过程中删除类型信息。例如,List将被转换为   非泛型类型List,通常包含任意对象。   编译时检查保证生成的代码是   型是正确的。

此类型信息适用于编译器和IDE。 Groovy基于Java,并且继承了泛型的相同原则。

另一方面,Groovy是更动态的语言,所以可能是它在编译时不检查类型的原因。 IMO for Groovy它是某种代码注释,有时非常有用。

PS @tim_yates建议链接到Groovy docs about Generics,确认:

  

Groovy目前做得更进一步,并且“在源级别”抛弃了泛型信息。