/* * The following is intended to be a guided exploration of the Groovy language. * It makes use of both the Groovy Shell and the Groovy Console. * The comments attempt to explain what each command is trying to convey. */ // *** Using groovysh *** // // Groovy is a pure object oriented language (it has no primitives like Java) 1.class // No 'int' primitive 1.0.class // No 'float' primitive (1 + 1.0).class // Automatic marshalling of results into appropriate class true.class // No 'boolean' primitive // Groovy extends the java.lang.String class in a useful way a.k.a. GStrings "asdf" // Declarations using double quotes are interpreted as a GString 'asdf' // Declarations using single quotes are interprested as a String 'a'.class // In Java this would be a 'char' primitive name = 'qldjvm' "Hello $name" // Simple variable substitution within a GString "Hello ${name.toUpperCase()}" // Use braces to surround expressions 'Hello $name' // The substitution doesn't work with java.lang.String println "Hello $name" // You can use the println() method without System.out println """Hello $name Say something interesting. Goodbye!""" // Use 3 double quotes to declare multi-line GStrings // Groovy has an expanded concept of boolean truth a.k.a. Groovy Truth // Note that 'as' can be used to cast classes 0 as boolean // zero values are false 1 as boolean // non-zero values are true -1 as boolean // and that includes negative non-zero values 0.0 as boolean // zero values are false 0.1 as boolean // non-zero values are true [] as boolean // empty lists are false [1] as boolean // non-empty lists are true [null] as boolean // a list of nulls is still a non-empty list [:] as boolean // empty maps are false [a:1] // non-empty maps are true // In Groovy, the assert keyword creates a PowerAssertion which has awesome debug output x = 1 y = 2 assert x == y // fails and prints the values of each term in the expression boolean odd(int n) {return n % 2 > 0} // declare a method to test for odd values assert odd(x) && odd(y) // also shows the input and return values of method calls /* Groovy supports closures which are first class functions (they can be passed * around as data). Closures are used all over the place in Groovy to do all * kinds of useful things. They often replace cases where anonymous inner classes * would have been used. */ closure = {println "whoop di do"} // Assign a block of code to a variable closure.call() // Long winded way of executing that block of code closure() // Simpler and more natural way of executing that block of code 3.times(closure) // Lots of methods accept closures as parameters transpose = {c -> println c} // Closures themselves can accept parameters transpose('a') // Looks just like a regular call to a method with parameters 'qldjvm'.each(transpose) // transpose is called on each character in the string /* There are a couple of things to point out in the next example: * 1. If a closure is the last parameter to a method, then you can omit the * parentheses. * 2. When dealing with closures that iterate over collections, the element * currently being iterated over is implicitly supplied to the closure as 'it' */ 'qldjvm'.each {println it} // Does the same as the previous example, but neater! /* Collections are real workshorses of programming languages and Groovy has * excellent support for lists. */ [] // An empty list [].class // The implementation is an ArrayList [1] // An array with a single element list = [1,2,3,4,5,6,7,8] // declaring lists with content is a single call [1, 'a'] // Lists can be heterogeneous (of mixed types) [] + ['a'] // '+' operator concatenates lists ['a', 1, 'a'] - 'a' //'-' operator returns the difference between lists [] << "a" << "b" // '<<' operator appends to a list (modifying the original) list * 2 // '*' operator repeats a list list[1] // list access using an index value list[0..2] // can retrieve a sublist by specifying a range list[1, 3, 5] // can also specify multiple indices list[-1] // negative index values work from the end of the list list[-3..-1] // and can be used in ranges too for (x in list) println x // iterates over each element in the list list.each {println it} // also iterates over each element in the list [[1,2],[3,4]].flatten() // collapses multi-dimensional lists into one dimension [1,2,3].intersect([2,3,4]) // returns the intersection of two lists [1,2,3].disjoint([2,3,4]) // returns true if two lists have no intersection [1,2,3].disjoint([4,5,6]) list.reverse() // reverses the order of the list [4,2,5,1].sort() // sorts the list [1, 'b', 'a', 2.0].sort() // sort works on heterogeneous lists too [2.0, 'bb', 'a', 1].sort {it.toString().size()} // you can pass your own calculation to sort the list by in a closure list.findAll {x -> odd(x)} // finds all the elements within the list for which the supplied closure returns true list.findAll {odd(it)} // shorthand version of above list.collect {it * 2} // returns a list with the supplied closure applied to each of the elements list.collect {it * 2}.findAll {it % 4 == 0} // calls can be chained [5,5,3,4,1,1,2].unique() // removes duplicates from the list [2,2,3,3,4,4,4].count(4) // counts the number of occurences of the supplied value list.min() // returns the minimum value from the list list.max() // returns the maximum value from the list list.every {odd(it)} // returns true if every element of the list returns true when passed to the supplied closure list.every {it > 0} list.any {odd(it)} // returns true if any element of the list returns true when passed to the supplied closure list.join(",") // creates a string by concatenating all of the elements of the list with the supplied string as a separator, in this case a comma list.join("|") // creates a pipe separated string representation of the list list.join("\t") // creates a tab separated string representation of the list list.sum() // adds all the values in the list together /* The inject() method is a fold from functional programming. * It reduces the list to a single value by providing a starting value and an * operation (in the form of a closure) that will combine all of the values in * the list. * In this example, runningTotal is given the value 0 on the first iteration, and * value is the first element in the list. They are added together and the result * becomes runningTotal for the second iteration, where value is the second element, * and so on until the end of the list is reached. This example shows how sum() * could be implemented. */ list.inject(0) {runningTotal, value -> runningTotal + value} // Groovy also has excellent support for maps [:] // an empty map map = [a:1, b:2, c:3] // declaring a map, the keys are converted to strings map['a'] // you can access elements using array syntax map[a] // since the keys are converted to strings, this won't work though map['d'] = 4 // you can add new key/value pairs using array syntax map['a'] = 11 // you can update values using array syntax a = 'something completely different' [(a):42] // if you want to use the content of a variable as a key, you need to surround it with parentheses [a:1] // otherwise it will be converted to a string map.b // can also access values using record syntax map.get('b') // or do it the old fashioned way! map.get('e', 0) // you can specify a default value if the key isn't in the list already map // using a default value adds the key automatically map.f = 6 // can add or update values using record syntax too /* The same collection functions that apply to lists also apply to maps. The * closures accept one or two values though. If one value is supplied (or the * implicit value 'it' is used) then it will be an object with 'key' and 'value' * properties. If 2 values are supplied, the first is the key and the second is * the value. */ map.each {println "key=${it.key}, value=${it.value}"} // explicit single parameter map.each {key, value -> println "key=$key, value=$value"} // implicit single parameter // You can embed the Groovy Console in your own application for debugging etc. myVar = [1,2,3] console = new groovy.ui.Console() console.setVariable("myVar", myVar) console.run() // *** Switch to groovyConsole *** // // In the top window, enter and then execute the script (Ctrl+Enter) myVar << 4 << 5 << 6 // *** Switch back to the Groovy Shell *** // myVar // the variable in the host application was modified // *** Switch to groovyConsole *** // /* Mixins are a way of combining functionality from one class into another one. * It might help to think of them (from a design point of view) as interfaces * with implementations. Groovy provides support for both compile time and run * time mixins. */ // *** START OF SCRIPT *** // // This is the class we want to mixin class Logger { void log(String msg) { println "> $msg" } } // This is the class receiving the mixin (at runtime) class Foo { static { // static initializer ensures the mixin is done before object creation Foo.class.mixin(Logger) } } foo = new Foo() foo.log('Look Ma... I got me a log method!') // We can also add mixins to existing classes at runtime myList = [1,2,3] List.mixin(Logger) myList.log('I log you') // Note that it affects existing objects, not just new ones // Use the @Delegate annotation to add a mixin at compile time class Bar { @Delegate Logger logger = new Logger() } bar = new Bar() bar.log('Delegation... for the nation!') // *** END OF SCRIPT *** // /* We can also extend the functionality of classes at runtime using the * ExpandoMetaClass. In this case we extend the Collection metaClass with a * pickOne() method that randomly selects an element from the collection. */ // *** START OF SCRIPT *** // myList = [1,2,3,4,5] Collection.metaClass.pickOne = { delegate[new Random().nextInt(delegate.size())] } 20.times {println myList.pickOne()} // *** END OF SCRIPT *** // /* Groovy provides a nice abstraction over JDBC which takes most of the headaches * away and gives you a viable alternative to using an ORM. * Note that to run this example you will need to include the hsqldb.jar on the * classpath (groovyConsole -cp /hsqldb.jar). */ // *** START OF SCRIPT *** // import groovy.sql.Sql db = Sql.newInstance("jdbc:hsqldb:mem:qldjvm") // Creates the database instance // Execute a SQL statement //db.execute "drop table PERSON" db.execute """ create table PERSON ( FIRST_NAME varchar(30), SURNAME varchar(30), AGE integer ) """ // Here we create a list and iterate over it, inserting the values into the database [ ['Fred', 'Flintstone', 42], ['Barney', 'Rubble', 38], ['Pebbles', 'Flinstone', 1], ['Bam Bam', 'Rubble', 2] ].each {db.execute "insert into PERSON values (${it[0]}, ${it[1]}, ${it[2]})"} // Run a query against the database result = db.rows("select * from PERSON") // Query results are returned as a list of maps result.each {println it} // We can access any row of the results directly println "The third result is ${result[2]}" // We can even make the results look like objects barney = db.rows("select * from PERSON where FIRST_NAME='Barney'").head() println "${barney.FIRST_NAME} ${barney.SURNAME} is ${barney.AGE} years old" // Drop the table so we can run the script again db.execute "drop table PERSON" // *** END OF SCRIPT *** //