Groovy in 15 Minutes - An Overview

    Groovy is an object-oriented programming language developed for the Java platform as an alternative to the Java language with the capabilities of Python, Ruby, and Smalltalk.

    Groovy uses Java-like syntax with JVM dynamic compilation of bytecode and works directly with other Java code and libraries. The language can be used in any Java project or as a scripting language.

    Groovy features (distinguishing it from Java):

    - Static and dynamic typing
    - Built-in syntax for lists, associative arrays, arrays and regular expressions
    - Closures
    - Overloading operations

    [ http://ru.wikipedia.org/wiki/Groovy ]

    Moreover, almost always java code is valid groovy code.


    Installation


    To install, you need to download the archive from of. site , unzip it to a convenient place and add the environment variable GROOVY_HOME, and add the path to groovy / bin in PATH:
    export GROOVY_HOME=~/path/to/groovy/
    export PATH=$GROOVY_HOME/bin:$PATH
    


    In NetBeans IDE 7.0, groovy support comes out of the box, for the Eclipse IDE there is a very good plugin that you can get here .

    Groovy


    The most important difference from java: in Groovy, everything is an object. All primitive types are immediately packed into objects. Those. "Int x" is actually "Integer x"

    println 1.class
    int a = 10
    println a.class
    


    class java.lang.Integer
    class java.lang.Integer
    


    You should not forget that all packaging types are immutable, so each time a calculation will create a new object.

    Lines in Groovy


    1) Java Strings - strings in single quotes
    2) Groovy Strings, they are GStrings - in ordinary quotes
    You can insert parameters in groovy strings, you can’t insert parameters in ordinary strings

    javaString = 'java'
    groovyString = "${javaString}"
    j = '${javaString}' 
    bigGroovyString = """
        ${javaString}
        ${groovyString}
        ${j}
        ${2 + 2}
    """
    println bigGroovyString
    

        java
        java
        ${javaString}
        4
    


    + And * operations are applicable to strings
    groovy:000> a = "a"
    ===> a
    groovy:000> a + "123"
    ===> a123
    groovy:000> a * 5    
    ===> aaaaa
    


    Also, ++ and - - apply to strings (since groovy supports operator overloading)
    groovy:000> a = 'abc'
    ===> abc
    groovy:000> a++
    ===> abd
    groovy:000> a--
    ===> abс
    


    Groovy has a set of regular expressions at the level of language constructs:
    groovy:000> r =~ '^a$'
    ===> java.util.regex.Matcher[pattern=^a$ region=0,1 lastmatch=]
    


    Native support for maps + lists


    Dictionaries (maps) and lists are also supported at the level of language constructs:
    groovy:000> a = [1, 3, 5]
    ===> [1, 3, 5]
    groovy:000> b = [1: true, 0: false]
    ===> {1=true, 0=false}
    


    Ranges


    You can access list items in groovy as follows:
    groovy:000> a = "0123456789"
    ===> 0123456789
    groovy:000> a[1..4]
    ===> 1234
    groovy:000> a[1..-1]
    ===> 123456789
    groovy:000> a[-1..0]
    ===> 9876543210
    groovy:000> a[1..<9]
    ===> 12345678
    groovy:000> a[1, 3, 5]
    ===> 135
    groovy:000> b = 1..5
    ===> 1..5
    groovy:000> a[b]
    ===> 12345
    


    Range is the same object, so constructions like the latter are possible. Negative indexes, as in python, return elements from the end of the list.

    Range can be composed of lines:
    groovy:000> 'a'..'aa'
    ===> a..aa
    


    Moreover, range can be made from any object that has the next () and prev () methods.

    Cycles


    The loops in groovy are exactly the same as in java, plus another “foreach” is added to them:
    for (i in 0..9) { 
        print i 
    }
    for (int i = 0; i < 9; ++i) {
        print i
    }
    for (Integer i : 0..9) { 
        print i 
    }
    


    Functions


    def functionA(argA) {
        print ArgA
    }
    int functionB(int argB) {
        print argB
        return argB
    }
    String fuctionC() {
        "Hello World"
    }
    


    The return keyword is optional; by default, the value of the last mentioned variable in the function will be returned.

    Closures


    Closure is an anonymous function.
    def cl = {a, b ->
        println a
        println b
    }
    cl(1, 2)
    


    Many objects have methods for which closure is passed as parameters:
    1.upto 10, { 
        print it
    }
    10.times { 
        print it
    }
    


    A large number of methods are available for processing sequences to which closures can be applied:

    'qwerty'.each {
        print it
    }
    ('a'..'z').each {
        print it
    }
    ('a'..'z').findAll { el -> // = filter
        el in ['e', 'y', 'u', 'i', 'o', 'a']
    }.each {
        print it + ' '
    }
    (0..10).collect { el -> // = map
        el * 10
    }.each {
        print it + ' '
    }
    def sum = (0..10).inject(0) { prev, elem -> // = reduce 
        return prev + elem    
    }
    


    It is also not necessary to use the return keyword in closure. If the parameter name is not explicitly specified, then it is used by default.

    Since closure is an object, nothing prevents returning it from another closure, and thus create higher-order functions:
    def cloA = {param ->
        def cloB = {
            return param * 10    
        }
    }
    def b = cloA(10)
    println b(10)
    


    Files



    There are eachFile and eachFileRecursive functions for directories:

    new File('.').eachFile {
        println it
    }
    

    ./.project
    ./src
    ./.settings
    ./.classpath
    ./bin
    


    To process text files - eachLine function:
    new File('textfile.txt').eachLine {
        println it
    }
    


    Writing to files is also very convenient:
    def pw = new File('textfile.txt').newPrintWriter()
    pw.println("new line")
    


    Classes


    class Account {
        String name
        BigDecimal value
    }
    // конструктор по умолчанию добавляется автоматически
    // такой конструктор - синтаксический сахар для 
    // a = new Account()
    // a.setName("Account #1")
    // a.setValue(new BigDecimal(10))
    a = new Account(name : "Account #1", value : new BigDecimal(10))
    // геттеры и сеттеры генерируются автоматически
    def name = a.getName()
    a.setName("Account #2")
    println "${a.name}"
    class Person {
        def first
        def last
        // явно задаем сеттер
        void setFirst(first) {
            println "${this.first} is becoming ${first}"
            this.first = first
        }
    }
    p = new Person(first : "A", last : "G")
    // если обращаться к полю, то будет использоваться сеттер
    p.first = "C"
    println "${p.first} ${p.last}"
    // наследвание как в java
    class ExtendedAccount extends Account {
        def debt
        // задаем конструктор
        ExtendedAccount(name, value, debt) {
            setName(name)
            setValue(value)
            setDebt(debt)
        }
        def String toString() {
            "${name} ${value} ${debt}"
        }
    }
    // тут будет ошибка "Could not find matching constructor for: ExtendedAccount()"
    //e = new ExtendedAccount()
    println new ExtendedAccount("A", new BigDecimal(10), 1)
    


    Immutable classes are defined using annotations Immutable :

    @Immutable
    class ImmutableClass {
        String a
        Integer b
    }
    def ic = new ImmutableClass(a : "a", b : 1)
    


    When using this annotation, you must explicitly indicate what type of data field.

    Operators



    "?:" Elvis operator
    def b = a ?: "b"
    

    Checks the variable a, and if it is null or false, it takes the value indicated next. Otherwise, the value of the variable a is taken.

    "?." Safe navigation
    Used to avoid a NullPointerException error
    def user = Users.get("a")
    def posts = user?.posts
    println posts 
    

    Will return null if user contains null instead of throwing a NullPointerException.

    "*." Spread operator
    Applies the specified method to all elements of a collection. Equivalent to the following:
    parent*.action == parent.collect {ch -> child?.action}
    


    Usage example:
    def sizes = ['string', 'long string']*.size()
    println sizes
    

    [6, 11]
    


    You can also use monjo to compile lists and dictionaries:
    def x = [2, 3]
    def y = [0, 1, *x, 4]
    println y
    def a = [3 : 'c', 4 : 'd']
    def b = [1 : 'a', 2: 'b', * : a, 5 : 'e']
    println b
    

    [0, 1, 2, 3, 4]
    [1:a, 2:b, 3:c, 4:d, 5:e]
    


    In Groovy, you can overload operators +, -, *, etc. To do this, you need to define the appropriate method for the class. For example, to overload the ++ operator, you need to override the next () method:

    class RandomVal {
        // для этого поля не будут сгенерированы сеттеры и геттеры
        private def value
        private Random randomGen = new Random()
        def next() {
            this.value = randomGen.nextInt()
        }
        RandomVal() {
            this.value = randomGen.nextInt()
        }
        def String toString() {
            "${this.value}"
        }
    }
    def r = new RandomVal()
    println(r)
    r++
    println(r)
    


    The operator "==" is already overloaded for all objects - and calls the "isEquals ()" method. A complete list of methods that must be overridden to overload operators is available here: http://groovy.codehaus.org/Operator+Overloading .

    SQL



    SQL queries are handled very simply:
    import groovy.sql.Sql
    def final ADDRESS = "jdbc:jtds:sqlserver://serverName/dbName"
    def final USERNAME = "username"
    def final PASSWD = "password"
    def final DRIVER = "net.sourceforge.jtds.jdbc.Driver"
    sql = Sql.newInstance(ADDRESS, USERNAME, PASSWD, DRIVER)
    sql.eachRow("select * from tableName") { el ->
        println "${el.id} -- ${el.firstName}"
    }
    def firstName = "A"
    def lastName = "G"
    sql.execute("insert into tableName (firstName, lastName) " + 
        "values (${firstName}, ${lastName})")
    sql.execute("insert into tableName (firstName, lastName) " +
        "values (?, ?)", [firstName, lastName])
    


    XML


    There are builders in groovy that can be used to generate XML. For generation, an instance of the MarkupBuilder object is created on which pseudo-methods are called - the name of this method and the parameters passed will be used to generate the tag:

    import groovy.xml.MarkupBuilder
    def mb = new MarkupBuilder()
    mb.html() {
        head() {
            title("This is the title")
        }
        body() {
            div("class" : "main") {
                p("this is the body")
            }
        }
    }
    

    Conclusion:
    This is the title

    this is the body



    You can pass any PrintWriter as a parameter to the MarkupBuilder constructor:
    def fb = new MarkupBuilder(new File("index.html").newPrintWriter())
    


    XML parsing is also very simple:
    import groovy.xml.MarkupBuilder
    import java.io.StringWriter
    def sw = new StringWriter()
    def mb = new MarkupBuilder(sw)
    mb.html() {   
        body() {
            div("class" : "main") {
                p("this is the body")
            }
            div() {
                p("this is the body 1")
                p("this is the body 2")
                p("this is the body 3")
            }
        }
    }
    def xml = sw.toString()
    println xml
    import groovy.util.XmlParser;
    def parser = new XmlParser()
    def doc = parser.parseText(xml)
    //def doc = parser.parse("index.html")
    println doc.body.div[1].p[1] // возвращает Node
    println doc.body.div[1].p // возвращает список, состоящий из Node
    println doc.body.div["@class"] // список значений аттрибута class для всех div
    


    Conclusion:

    this is the body

    this is the body 1

    this is the body 2

    this is the body 3

    p[attributes={}; value=[this is the body 2]] [p[attributes={}; value=[this is the body 1]], p[attributes={}; value=[this is the body 2]], p[attributes={}; value=[this is the body 3]]] [main, null]


    Groovlets


    Using the GroovyServlet class, it is possible to run scripts on Groovy as servlets.
    First of all, for this you need to add a few lines to web.xml:

    GroovyServletgroovy.servlet.GroovyServletGroovyServlet*.groovy


    Now all requests for .groovy files will be processed by the GroovyServlet class.
    The following variables are already available for use in these scripts:

    - request & response
    - context, application, session
    - out (= response.getWriter ())
    - sout (= response.getOutputStream ())
    - html (= new MarkupBuilder (out))

    html.html() {   
        body() {
            div("class" : "main") {
                p("this is the body")
            }
            div() {
                p("this is the body 1")
                p("this is the body 2")
                p("this is the body 3")
            }
        }
    }
    


    It will give the browser the generated html page.

    List of sources used:


    Kenneth Barclay, John Savage "Groovy programming: an introduction for Java developers"
    http://groovy.codehaus.org/

    Also popular now: