2014-02-24 65 views
14

問題1Groovy中的列表和列表<String>是否相同?

它是無關緊要是否List(對象列表)或List<String>(字符串列表)在Groovy中使用?

在下面的代碼示例中,兩個列表最終都是ArrayList(對象的ArrayList)。本來期望第二個列表是ArrayList<String>(字符串的ArrayList)。

當編譯類時,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放入一個字符串列表中。任何人都可以解釋爲什麼我可以添加一個intString-?

輸出顯示它仍爲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 
+1

http://stackoverflow.com/questions/15755261/groovy-map-and-java-map-on-generics –

+0

@tim_yates:那麼根據鏈路的回答你發佈是「[...]這意味着類型參數在編譯時不被檢查,並且在運行時不可用。」這意味着我不能阻止任何人將'int'放入'List '中,這意味着我所能確定的是'List '包含'object'類型的對象。正確? – Lernkurve

+3

是的。除非你使用'@ CompileStatic' –

回答

21

當運行的Groovy 「正常」,仿製藥被扔掉編譯之前,所以只能在源作爲有益的提醒開發商存在。

但是,您可以使用@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據說只有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這個類,這個錯誤會被拾取,但是我總是喜歡在我可以使用的地方使用它,而不僅僅是在我需要的地方。

+0

真棒回答先生耶茨! – mikemil

+1

@mikemil爲什麼感謝你:-) * /我需要一個弓* ;-) –

+0

如果我的方法在方法定義中返回一個[boolean,String]的列表,非常有用,我應該省略返回類型或將其作爲'列表? –

3

Wikipedia,爲Java:

泛型在編譯時間類型正確性檢查。然後在名爲type erasure的過程中刪除通用的 類型信息。例如,列表將被轉換爲 非泛型類型List,通常包含任意對象。 編譯時檢查保證生成的代碼爲 類型正確。

此類型信息用於編譯器和IDE。 Groovy基於Java並繼承了泛型的相同原則。另一方面,Groovy是更動態的語言,因此可能是它在編譯時不檢查類型的原因。 IMO for Groovy是一種代碼評論,有時非常有用。

PS @tim_yates提出了一個鏈接Groovy docs about Generics,即確認:

的Groovy目前確實有點更進一步,扔掉仿製藥信息「在源代碼級別」。

+0

我不認爲這是100%真實的。 http://groovy.codehaus.org/Generics –

3

正如@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 
相關問題