for collection array list arraylist groovy

list - collection - groovy map collect



¿La lista y la lista<String> son iguales en Groovy? (3)

Como @tim_yates observa, es posible habilitar las verificaciones de tiempo de compilación con las anotaciones @TypeChecked / @CompileStatic .

Otra alternativa es habilitar la verificación del tipo de tiempo de ejecución envolviendo la colección con Collections.checkedList() . Si bien esto no usa los genéricos o el tipo declarado, su cumplimiento en el tiempo de ejecución a veces encaja mejor con el código dinámico escrito de forma flexible. Esta es una característica de la plataforma Java no específica para groovy.

Ejemplo:

// 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

Pregunta 1

¿Es irrelevante si se utiliza una List (lista de objetos) o una List<String> (lista de Cadenas) en Groovy?

En el ejemplo de código a continuación, ambas listas terminan siendo una ArrayList (ArrayList de objetos). Habría esperado que la segunda lista fuera una ArrayList<String> (ArrayList of Strings).

¿Groovy pierde la información de tipo cuando se compila la clase y la deduce cuando se ejecuta la clase compilada?

Ejemplo 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()}"

Salida 1

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

Pregunta 2

Hubiera esperado que la línea typedList << new Integer(1) en el ejemplo a continuación falle con una excepción porque estoy tratando de poner un int en una lista de cadenas. ¿Alguien puede explicar por qué puedo agregar un int a la List tipos de String ?

La salida muestra que sigue siendo un Integer , es decir, no se convierte sobre la marcha a una String "1".

Ejemplo 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() }

Salida 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


Cuando se ejecuta Groovy "normalmente" , los genéricos se desechan antes de la compilación, por lo que solo existen en la fuente como recordatorios útiles para el desarrollador.

Sin embargo, puede usar @CompileStatic o @TypeChecked para hacer que Groovy @TypeChecked estos genéricos y verifique los tipos de cosas en la compilación.

Como ejemplo, considérese que tengo la siguiente estructura de proyecto:

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

Con el código:

construir.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 } }

Ahora, ejecutar gradle runSimple nos da la salida:

:compileJava UP-TO-DATE :compileGroovy :processResources UP-TO-DATE :classes :runSimple [1, tim] BUILD SUCCESSFUL Total time: 6.644 secs

Como puede ver, los genéricos se desecharon, y simplemente funcionó agregando Integers y Strings a la List de supuestamente solo Integers

Ahora, si cambiamos ListDelegate.groovy a:

package test import groovy.transform.* @CompileStatic class ListDelegate<T> { @Delegate List<T> numbers = [] }

Y corre otra vez:

:compileJava UP-TO-DATE :compileGroovy :processResources UP-TO-DATE :classes :runSimple [1, tim] BUILD SUCCESSFUL Total time: 6.868 secs

Obtenemos la misma salida !! Esto se debe a que, si bien ListDelegate ahora está compilado de forma estática, nuestra clase Main sigue siendo dinámica, por lo que aún desecha los genéricos antes de construir ListDelegate ... Por lo tanto, también podemos cambiar Main.groovy a:

package test import groovy.transform.* @CompileStatic class Main { static main( args ) { def del = new ListDelegate<Integer>() del << 1 del << ''tim'' println del } }

Y ahora, volviendo a ejecutar gradle runSimple danos:

: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

Lo que es, como cabría esperar, no agregar una String a nuestra Lista de Integer declarada.

De hecho, solo necesita CompileStatic la clase Main.groovy y este error se Main.groovy , pero siempre me gusta usarlo donde pueda, no solo donde lo necesito.


De Wikipedia , para Java:

Los genéricos se verifican en tiempo de compilación para ver si son correctos. La información de tipo genérico se elimina luego en un proceso llamado borrado de tipo . Por ejemplo, la Lista se convertirá a la Lista de tipo no genérico, que normalmente contiene objetos arbitrarios. La verificación en tiempo de compilación garantiza que el código resultante es de tipo correcto.

Esta información de tipo es para el compilador y el IDE. Groovy se basa en Java y hereda los mismos principios para los genéricos.

Por otro lado, Groovy es un lenguaje más dinámico, por lo que probablemente sea la razón por la que no verifica los tipos en el tiempo de compilación. OMI para Groovy es una especie de comentario de código, a veces muy útil.

PS @tim_yates sugirió un enlace a los documentos de Groovy sobre Genéricos , que confirma:

Groovy actualmente hace un poco más y desecha la información genérica "en el nivel de origen".