问题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
答案 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
正如您所看到的那样,泛型被丢弃了,它只是将Integers
和Strings
添加到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目前做得更进一步,并且“在源级别”抛弃了泛型信息。