Intro to the Groovy Language and Ecosystem

Current

Slides(source)

About Me

About YOU

  • Students
  • Professional Developers
    • Java
    • .NET
    • PHP or Ruby
    • JRuby, Scala, or Clojure
    • Groovy

Agenda

  • Background & Ecosystem
  • Getting Started
  • Concepts
    • Data Types
    • Collections/Loops
    • Functions
    • Closures

Agenda

  • Object Oriented Programming (OOP)
  • Scripting
  • File Processing
  • APIs
  • MetaProgramming

Groovy

  • Dynamic, compiled language for the Java Virtual Machine(JVM).
  • OPTIONALLY typed
  • Interoperable with existing Java libraries
  • Concise, easy to read code
  • Ability to be statically compiled
  • Used by many major companies including:

source: http://www.groovy-lang.org

Open Source

http://groovy-lang.org

https://github.com/apache/groovy

The Groovy Ecosystem

When to use Groovy

  • existing Java libraries
  • developers know Java or another JVM language
  • infrastructure to support JVMs
  • quick prototyping
  • scripting
  • writing tests

When Not to use Groovy

  • lots of legacy code in another language
  • need 'bare metal' level performance

Getting Started

Groovy Shell

Groovy Console

Web Console

http://groovyconsole.appspot.com/

Things to Remember

  1. There is more than one way to do almost anything.
  2. It's probably optional.
    • semi-colons
    • typing
    • parenthesis around parameters
    • getters and setters
    • return statements
  3. By default, the last command executed is what's returned.

Hello, World!

groovy>println "Hello, DevFest Berlin 2017!"

Hello, DevFest Berlin 2017!
===>null

Data Types

  • Optional Typing, NOT dynamic
  • treated as objects
    • int treated like Integer
int i = 10
println i.class.name
 -> java.lang.Integer
 
Integer i = 20
println i.class.name
 -> java.lang.Integer

def

when no type is specified or for a flexible type

def i = 10
println i * 2
 -> 20
 
i = 'abc'
println i * 2
 -> abcabc

Strings

(examples in console)

  • Single Quoted
  • Double Quoted
  • Multi-line

Collections

Lists

(examples in console)

  • definition
  • get/set values
  • append
  • multiply
List m = ['a', 'b', 2]
println m[2]
-> 2

m[2] = 3
println m
-> [a, b, 3]

m << 'c'
println m
-> [a, b, 3, c]

m.push(4)
-> [a, b, 3, c, 4]

m.pop()
-> 4
List m = ['a', 'b', 2]

m.multiply(2)
 -> ['a', 'b', 2, 'a', 'b', 2]
 
m * 2
 -> ['a', 'b', 2, 'a', 'b', 2]
 
m + 2
 -> ['a', 'b', 2, 2]

Maps

(examples in console)

  • definition
  • get/set values
  • get all keys/values
Map m = [val1: 'a', val2: 'b', val3: 2]

println m.val1
 -> a
println m['val3']
 -> 2
 
m.val4 = 3
println m
 -> [val1:a, val2:b, val3: 2, val4: 3]
 
println m.keySet()
 -> [val1, val2, val3, val4]
 
println m.values()
 -> [a, b, 2, 3]

Ranges

println (1..10)
  -> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
println (1..<10)
  -> [1, 2, 3, 4, 5, 6, 7, 8, 9]

Functions

Integer timesTwo(x) {
  x + x
}
  • Flexible, familiar syntax
  • Optional
    • Parenthesis when calling functions
    • Parameter typing
    • return statements
println timesTwo(10)
 -> 20
def result = timesTwo 10
println result
 -> 20

Closures

Code as Data

(to the console)

  • definition
  • calls

Closures

  • Passed as an argument
  • Assigned to a variable
  • Returned from a function
Closure c = { name, year = 2017 ->
  println "Hello, $name $year!"
}

c.call('DevFest Berlin')
-> "Hello, DevFest Berlin 2017!"

c('DevFest Berlin', 2018)
-> "Hello, DevFest Berlin 2018!"

Closure Scope

this

instance of the class where the closure was defined

owner

usually the same as this unless declared inside another closure

delegate

same as owner but can be reassigned when the closure is passed around

Loops and Special Functions

  • Basic For Loop
  • Each
  • Collect
  • Spread Dot

Basic Loops

List dl = []

for(Integer i = 1; i < 4; i++){
  dl.add(i * 2)
}

println dl
 -> [2, 4, 6]
List dl = []

for(i in [1,2,3]) {
  dl << i * 2
}

println dl
 -> [2, 4, 6]

Each

List sl = [1, 2, 3]
List dl = []
sl.each { it ->
  dl << it * 2
}
println dl
-> [2, 4, 6]

Collect

Map m = [va1: 1, var2: 2, var3: 3]

List dl = m.collect { key, value ->
  value * 2
}

println dl
 -> [2, 4, 6]

Spread Dot

(1..3).multiply(2) -> [1, 2, 3, 1, 2, 3](1..3)*.multiply(2) -> [2, 4, 6]

Try It!

Let's groovy-fy this code

List l = [-1,2,-3,-17]
List al = []
for ( i in l ) {
  al << i.abs()
}
println al
--> [1,2,3,17]
[-1,2,-3,-17]*.abs()

Try It!

Print the sum of the numbers from 1 to 10

option 1:
Integer answer = 0
for(i in [1,2,3,4,5,6,7,8,9,10]) {
  answer += i
}
println answer
option 2:
Integer answer = 0
(1..10).each {
  answer = answer + it
}
println answerbonus answer:
println ( (1..10).sum() )

Groovy Truth

False values

  • null
  • Empty Set
  • Empty String
  • Zero

Equality

== verifies value same as .equals()

is verifies object identity

List numbers = [0,1,2,3]
numbers.each {
  if(it) {
    println it
  }
}

-> 1
-> 2
-> 3

Identity

Integer myInteger = 2
int myInt = 2

myInteger.is(myInt)

-> true

Identity

String world = 'World'
GString myGString = "Hello, $world"
String myString = 'Hello, World'

println myGString
println myString

myGString.is(myString)

-> Hello, World
-> Hello, World
-> false

Other Groovy Operators

  • Ternary Operator
  • x ? return x : return 'something else'
  • Elvis Operator
  • x ?: 'something else'
  • Null Safe
  • myMap?.propertyA?.propertyB

Search

Map m = [val1: 2, val2: 4, val3: 6]

m.find { it.value % 2 == 0 && it.value % 3 != 0 }
-> val1=2

m.findAll {it.value % 2 == 0 && it.value % 3 != 0 }
-> [val1:2, val2:4]

Other List Methods

  • Any
  • Map m = [val1: 2, val2: 4, val3: 6]
    m.any { it.value % 2 == 0 }
    -> true
  • Every
  • m.every { it.value % 3 == 0 }
    -> false

Object Oriented Programming (OOP)

class Person {
  String first
  String last
}
Person me = new Person(first: 'Jane', last: 'Doe')
println me.first
println me.last

-- add String company to class --
me.company = 'Abc Inc'
println me.properties
-> [class:class Person, first:Jane, company:Abc Inc, last:Doe]
class Person {
  String first
  String last
  
  String getFullName() {
    this.first + ' ' + this.last
  }
}

Person me = new Person(first: 'Jane', last: 'Doe')
println me.fullName
 -> 'Jane Doe'

Scripts

(save previous script as people.groovy)

groovy people.groovy
-> Jane Doe

Scripts

println doubledValue(10)

def doubledValue(x) {
  x * 2
}

AST Viewer

File Processing

Creating and Writing to Files

def myFile = new File('foo.txt')
myFile.write 'Hello, \n'
myFile.append('DevFest Berlin!')

foo.txt

Hello,
DevFest Berlin!

Reading Files

def myFile = new File('foo.txt')
myFile.eachLine { line ->
  def processedLine = line.replaceAll('Hello', 'Hi')
  println processedLine
}

 -> Hi,
 -> DevFest Berlin!

APIs

(copy to console)

@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.1')
import groovyx.net.http.RESTClient
def http = new RESTClient("http://www.gr8ladies.org")
http.get(path: '/api/companies') { resp, reader ->
  reader.sort { -it.percentageTotalWomen } // sort by percentage women in descending order
  reader[0..4].each{ // only the first 5
    if(it.totalEmployees) { // if the value of total employees is not 0 (to eliminate companies that didn't report that field)
      println 'name:' + it.name
      println 'country:' + it.country
      println 'total employees: ' + it.totalEmployees
      println 'total men: '+ it.totalMen
      println 'total women: '+ it.totalWomen + '\n'
    }
  }
}
return null // don't print the entire result at the end

APIs

(copy to console)

@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.1')

import groovyx.net.http.RESTClient

def http = new RESTClient("http://api.icndb.com")

http.get(path: '/jokes/random/5', query: [exclude: 'explicit']) { resp, reader ->
  reader.value.joke.each{
    println it + '\n'
  }
}

MetaProgramming

Extending a class

Number.metaClass.getDollars = { delegate as BigDecimal }

Number.metaClass.getProperty = { String name ->
  def rates = [euros: 1.1f, pesos: 0.063f]
  delegate * (rates[name] as BigDecimal)
}

Adding different currencies together

def total = 20.dollars + 40.euros + 200.pesos
assert total == 76.60

MetaProgramming

Dynamic Methods

class Person {
  def methodMissing(String name, args) {
    if (name.startsWith('say')) {
      String message = (name - 'say').trim()
      println message
    }
  }
}

Trying out our dynamic method

Person you = new Person()
you.sayHello()
you."say You are awesome!"()

Other Cool Groovy Things

  • Regex
  • Groovy for Android
  • Compile Static Annotation
  • Domain Specific Language (DSL)s

Your first app

sdk use gradle 4.3

Your first app

gradle init --type=groovy-application

Your first app

(example in IntelliJ)

  • Main
  • Spock Test
  • Build, Test, and Run with Gradle

Conclusions

    Groovy
  • provides the benefits of the JVM platform
  • adds features to enhance productivity over Java
  • is similar to other languages such as Ruby and Python

Resources

Questions?