//---------------------------------------------------------------------------------- def hello() { greeted += 1 println "hi there!" } // We need to initialize greeted before it can be used, because "+=" assumes predefinition greeted = 0 hello() println greeted // => // hi there // 1 //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // basic method calling examples // In Groovy, parameters are named anyway def hypotenuse(side1, side2) { Math.sqrt(side1**2 + side2**2) // sqrt in Math package } diag = hypotenuse(3, 4) assert diag == 5 // the star operator will magically convert an Array into a "tuple" a = [5, 12] assert hypotenuse(*a) == 13 // both = men + women // In Groovy, all objects are references, so the same problem arises. // Typically we just return a new object. Especially for immutable objects // this style of processing is very common. nums = [1.4, 3.5, 6.7] def toInteger(n) { n.collect { v -> v.toInteger() } } assert toInteger(nums) == [1, 3, 6] orignums = [1.4, 3.5, 6.7] def truncMe(n) { (0..<n.size()).each{ idx -> n[idx] = n[idx].toInteger() } } truncMe(orignums) assert orignums == [1, 3, 6] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // variable scope examples def somefunc() { def variableInMethod // private is default in a method } def name // private is default for variable in a script bindingVar = 10 // this will be in the binding (sort of global) globalArray = [] // In Groovy, run_check can't access a, b, or c until they are // explicitely defined global (using leading $), even if they are // both defined in the same scope def checkAccess(x) { def y = 200 return x + y + bindingVar // access private, param, global } assert checkAccess(7) == 217 def saveArray(ary) { globalArray << 'internal' globalArray += ary } saveArray(['important']) assert globalArray == ["internal", "important"] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // you want a private persistent variable within a script method // you could use a helper class for this class CounterHelper { private static counter = 0 def static next() { ++counter } } def greeting(s) { def n = CounterHelper.next() println "Hello $s (I have been called $n times)" } greeting('tom') greeting('dick') greeting('harry') // => // Hello tom (I have been called 1 times) // Hello dick (I have been called 2 times) // Hello harry (I have been called 3 times) // you could make it more fancy by having separate keys, // using synchronisation, singleton pattern, ThreadLocal, ... //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Determining Current Method Name // Getting class, package and static info is easy. Method info is just a little work. // From Java we can use: // new Exception().stackTrace[0].methodName // or for Java 5 and above (saves relatively expensive exception creation) // Thread.currentThread().stackTrace[3].methodName // But these give the Java method name. Groovy wraps its own runtime // system over the top. It's still a Java method, just a little bit further up the // stack from where we might expect. Getting the Groovy method name can be done in // an implementation specific way (subject to change as the language evolves): def myMethod() { names = new Exception().stackTrace*.methodName println groovyUnwrap(names) } def myMethod2() { names = Thread.currentThread().stackTrace*.methodName names = names[3..<names.size()] // skip call to dumpThread println groovyUnwrap(names) } def groovyUnwrap(names) { names[names.indexOf('invoke0')-1] } myMethod() // => myMethod myMethod2() // => myMethod2 // Discussion: If what you really wanted was a tracing mechanism, you could overrie // invokeMethod and print out method names before calling the original method. Or // you could use one of the Aspect-Oriented Programming packages for Java. //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Passing Arrays and Hashes by Reference // In Groovy, every value is a reference to an object, thus there is // no such problem, just call: arrayDiff(array1, array2) // pairwise add (altered so it doesn't require equal sizes) def pairWiseAdd(a1, a2) { s1 = a1.size(); s2 = a2.size() (0..<[s1,s2].max()).collect{ it > s1-1 ? a2[it] : (it > s2-1 ? a1[it] : a1[it] + a2[it]) } } a = [1, 2] b = [5, 8] assert pairWiseAdd(a, b) == [6, 10] // also works for unequal sizes b = [5, 8, -1] assert pairWiseAdd(a, b) == [6, 10, -1] b = [5] assert pairWiseAdd(a, b) == [6, 2] // We could check if both arguments were of a particular type, e.g. // (a1 instanceof List) or (a2.class.isArray()) but duck typing allows // it to work on other things as well, so while wouldn't normally do this // you do need to be a little careful when calling the method, e.g. // here we call it with two maps of strings and get back strings // the important thing here was that the arguments were indexed // 0..size-1 and that the items supported the '+' operator (as String does) a = [0:'Green ', 1:'Grey '] b = [0:'Frog', 1:'Elephant', 2:'Dog'] assert pairWiseAdd(a, b) == ["Green Frog", "Grey Elephant", "Dog"] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Detecting Return Context // There is no exact equivalent of return context in Groovy but // you can behave differently when called under different circumstances def addValueOrSize(a1, a2) { b1 = (a1 instanceof Number) ? a1 : a1.size() b2 = (a2 instanceof Number) ? a2 : a2.size() b1 + b2 } assert (addValueOrSize(10, 'abcd')) == 14 assert (addValueOrSize(10, [25, 50])) == 12 assert (addValueOrSize('abc', [25, 50])) == 5 assert (addValueOrSize(25, 50)) == 75 // Of course, a key feature of many OO languages including Groovy is // method overloading so that responding to dofferent parameters has // a formal way of being captured in code with typed methods, e.g. class MakeBiggerHelper { def triple(List iList) { iList.collect{ it * 3 } } def triple(int i) { i * 3 } } mbh = new MakeBiggerHelper() assert mbh.triple([4, 5]) == [12, 15] assert mbh.triple(4) == 12 // Of course with duck typing, we can rely on dynamic typing if we want def directTriple(arg) { (arg instanceof Number) ? arg * 3 : arg.collect{ it * 3 } } assert directTriple([4, 5]) == [12, 15] assert directTriple(4) == 12 //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Passing by Named Parameter // Groovy supports named params or positional arguments with optional // defaults to simplify method calling // named arguments work by using a map def thefunc(Map args) { // in this example, we just call the positional version thefunc(args.start, args.end, args.step) } // positional arguments with defaults def thefunc(start=0, end=30, step=10) { ((start..end).step(step)) } assert thefunc() == [0, 10, 20, 30] assert thefunc(15) == [15, 25] assert thefunc(0,40) == [0, 10, 20, 30, 40] assert thefunc(start:5, end:20, step:5) == [5, 10, 15, 20] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Skipping Selected Return Values // Groovy 1.0 doesn't support multiple return types, so you always use // a holder class, array or collection to return multiple values. def getSystemInfo() { def millis = System.currentTimeMillis() def freemem = Runtime.runtime.freeMemory() def version = System.getProperty('java.vm.version') return [millis:millis, freemem:freemem, version:version] // if you are likely to want all the information use a list // return [millis, freemem, version] // or dedicated holder class // return new SystemInfo(millis, freemem, version) } result = getSystemInfo() println result.version // => 1.5.0_08-b03 //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Returning More Than One Array or Hash // As per 10.8, Groovy 1.0 doesn't support multiple return types but you // just use a holder class, array or collection. There are no limitations // on returning arbitrary nested values using this technique. def getInfo() { def system = [millis:System.currentTimeMillis(), version:System.getProperty('java.vm.version')] def runtime = [freemem:Runtime.runtime.freeMemory(), maxmem:Runtime.runtime.maxMemory()] return [system:system, runtime:runtime] } println info.runtime.maxmem // => 66650112 (info automatically calls getInfo() here) //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Returning Failure // This is normally done in a heavy-weight way via Java Exceptions // (see 10.12) or in a lightweight way by returning null def sizeMinusOne(thing) { if (thing instanceof Number) return thing.size() - 1 } def check(thing) { result = sizeMinusOne(thing) println (result ? "Worked with result: $result" : 'Failed') } check(4) check([1, 2]) check('abc') // => // Failed // Worked with result: 1 // Worked with result: 2 //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Prototyping Functions: Not supported by Groovy but arguably // not important given other language features. // Omitting Parentheses Scenario: Groovy only lets you leave out // parentheses in simple cases. If you had two methods sum(a1,a2,a3) // and sum(a1,a2), there would be no way to indicate that whether // 'sum sum 2, 3, 4, 5' meant sum(sum(2,3),4,5) or sum(sum(2,3,4),5). // You would have to include the parentheses. Groovy does much less // auto flattening than some other languages; it provides a *args // operator, varargs style optional params and supports method // overloading and ducktyping. Perhaps these other features mean // that this scenario is always easy to avoid. def sum(a,b,c){ a+b+c*2 } def sum(a,b){ a+b } // sum sum 1,2,4,5 // => compilation error sum sum(1,2),4,5 sum sum(1,2,4),5 // these work but if you try to do anything fancy you will run into trouble; // your best bet is to actually include all the parentheses: println sum(sum(1,2),4,5) // => 17 println sum(sum(1,2,4),5) // => 16 // Mimicking built-ins scenario: this is a mechanism to turn-off // auto flattening, Groovy only does flattening in restricted circumstances. // func(array, 1, 2, 3) is never coerced into a single list but varargs // and optional args can be used instead def push(list, Object[] optionals) { optionals.each{ list.add(it) } } items = [1,2] newItems = [7, 8, 9] push items, 3, 4 push items, 6 push (items, *newItems) // brackets currently required, *=flattening // without *: items = [1, 2, 3, 4, 6, [7, 8, 9]] assert items == [1, 2, 3, 4, 6, 7, 8, 9] //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Handling Exceptions // Same story as in Java but Groovy has some nice Checked -> Unchecked // magic behind the scenes (Java folk will know what this means) // When writing methods: // throw exception to raise it // When calling methods: // try ... catch ... finally surrounds processing logic def getSizeMostOfTheTime(s) { if (s =~ 'Full Moon') throw new RuntimeException('The world is ending') s.size() } try { println 'Size is: ' + getSizeMostOfTheTime('The quick brown fox') println 'Size is: ' + getSizeMostOfTheTime('Beware the Full Moon') } catch (Exception ex) { println "Error was: $ex.message" } finally { println 'Doing common cleanup' } // => // Size is: 19 // Error was: The world is ending // Doing common cleanup //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Saving Global Values // We can just save the value and restore it later: def printAge() { println "Age is $age" } age = 18 // binding "global" variable printAge() // => 18 if (age > 0) { def origAge = age age = 23 printAge() // => 23 age = origAge } printAge() // => 18 // Depending on the circmstances we could enhance this in various ways // such as synchronizing, surrounding with try ... finally, using a // memento pattern, saving the whole binding, using a ThreadLocal ... // There is no need to use local() for filehandles or directory // handles in Groovy because filehandles are normal objects. //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Redefining a Function // This can be done via a number of ways: // OO approach: // The standard trick using OO is to override methods in subclasses class Parent { def foo(){ println 'foo' } } class Child extends Parent { def foo(){ println 'bar' } } new Parent().foo() // => foo new Child().foo() // => bar // Category approach: // If you want to redefine a method from an existing library // you can use categories. This can be done to avoid name conflicts // or to patch functionality with local mods without changing // original code println new Date().toString() // => Sat Jan 06 16:44:55 EST 2007 class DateCategory { static toString(Date self) { 'not telling' } } use (DateCategory) { println new Date().toString() } // => not telling // Closure approach: // Groovy's closures let you have "anonymous methods" as objects. // This allows you to be very flexible with "method" redefinition, e.g.: colors = 'red yellow blue green'.split(' ').toList() color2html = new Expando() colors.each { c -> color2html[c] = { args -> "<FONT COLOR='$c'>$args</FONT>" } } println color2html.yellow('error') // => <FONT COLOR='yellow'>error</FONT> color2html.yellow = { args -> "<b>$args</b>" } // too hard to see yellow println color2html.yellow('error') // => <b>error</b> // Other approaches: // you could use invokeMethod to intercept the original method and call // your modified method on just particular input data //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Trapping Undefined Function Calls class FontHelper { // we could define all the important colors explicitly like this def pink(info) { buildFont('hot pink', info) } // but this method will catch any undefined ones def invokeMethod(String name, Object args) { buildFont(name, args.join(' and ')) } def buildFont(name, info) { "<FONT COLOR='$name'>" + info + "</FONT>" } } fh = new FontHelper() println fh.pink("panther") println fh.chartreuse("stuff", "more stuff") // => // <FONT COLOR='hot pink'>panther</FONT> // <FONT COLOR='chartreuse'>stuff and more stuff</FONT> //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Simulating Nested Subroutimes: Using Closures within Methods def outer(arg) { def x = arg + 35 inner = { x * 19 } x + inner() } assert outer(10) == 900 //---------------------------------------------------------------------------------- |
//---------------------------------------------------------------------------------- // Program: Sorting Your Mail #!/usr/bin/groovy import javax.mail.* // solution using mstor package (mstor.sf.net) session = Session.getDefaultInstance(new Properties()) store = session.getStore(new URLName('mstor:/path_to_your_mbox_directory')) store.connect() // read messages from Inbox inbox = store.defaultFolder.getFolder('Inbox') inbox.open(Folder.READ_ONLY) messages = inbox.messages.toList() // extractor closures subject = { m -> m.subject } subjectExcludingReplyPrefix = { m -> subject(m).replaceAll(/(?i)Re:\\s*/,'') } // double slash to single outside triple quotes date = { m -> d = m.sentDate; new Date(d.year, d.month, d.date) } // ignore time fields // sort by subject excluding 'Re:' prefixs then print subject for first 6 println messages.sort{subjectExcludingReplyPrefix(it)}[0..5]*.subject.join('\n') // => // Additional Resources for JDeveloper 10g (10.1.3) // Amazon Web Services Developer Connection Newsletter #18 // Re: Ant 1.7.0? // ARN Daily | 2007: IT predictions for the year ahead // Big Changes at Gentleware // BigPond Account Notification // sort by date then subject (print first 6 entries) sorted = messages.sort{ a,b -> date(a) == date(b) ? subjectExcludingReplyPrefix(a) <=> subjectExcludingReplyPrefix(b) : date(a) <=> date(b) } sorted[0..5].each{ m -> println "$m.sentDate: $m.subject" } // => // Wed Jan 03 08:54:15 EST 2007: ARN Daily | 2007: IT predictions for the year ahead // Wed Jan 03 15:33:31 EST 2007: EclipseSource: RCP Adoption, Where Art Thou? // Wed Jan 03 00:10:11 EST 2007: What's New at Sams Publishing? // Fri Jan 05 08:31:11 EST 2007: Building a Sustainable Open Source Business // Fri Jan 05 09:53:45 EST 2007: Call for Participation: Agile 2007 // Fri Jan 05 05:51:36 EST 2007: IBM developerWorks Weekly Edition, 4 January 2007 // group by date then print first 2 entries of first 2 dates groups = messages.groupBy{ date(it) } groups.keySet().toList()[0..1].each{ println it println groups[it][0..1].collect{ ' ' + it.subject }.join('\n') } // => // Wed Jan 03 00:00:00 EST 2007 // ARN Daily | 2007: IT predictions for the year ahead // EclipseSource: RCP Adoption, Where Art Thou? // Fri Jan 05 00:00:00 EST 2007 // Building a Sustainable Open Source Business // Call for Participation: Agile 2007 |