I have been working using Groovy by Jenkins pipelines for extended period of time. As now I am moving more to Java world it is good time for a short summary of the capabilities of this language. In my view Groovy is a U-language: unknown, underrated and unnoticed. Many people heard about it but only few know how handy and universal it is.
In general, Groovy is like Java. It is JVM language but also it understands pure Java code. This is one of the benefits as Java people can use it instantly. In contrast to Java, it can be used in the context of the most, if not all, programming paradigms allowing for almost free structuring of the codebase. Let me show you few very simple examples to illustrate how flexible it is.
A. Imperative programming.
This is when commands are used to describe how the program performs step by step. It seems to be the most popular way to write computer programs.
1. Scripting language.
Groovy can be used as plain scripting language. You can write instructions one by one to achieve the desired result. This is like bash:
println "this is just plain scripting"
a = 1
b = 2
println a + b
2. Object oriented programming with dynamic types.
One can go into OO world and use objects with dynamic types like this:
class Car {
def regNumber = "123AB"
def numberOfStarts = 0
def weight = 2
def start() {
numberOfStarts++
println "running"
}
def stop() {
println "stopping"
}
def getRegNumber() {
return regNumber
}
}
def car = new Car()
car.start()
car.start()
assert car.getNumberOfStarts() == 2
println car.getRegNumber()
car = "this is car"
println car
When static typing is overkill you can simplify the code and avoid it. Dynamic typing also means you can assign multiple value types to the same variable like def car. It may also return different types from the same def method() as the type is selected during runtime only (not shown in the example).
3. Object oriented programming using static types.
One can be more like Java developer and use static types as well (notice, this is not Java, as no semicolons here):
class Plane {
String regNumber = "123AB"
int weight = 20
int numberOfFlights
void fly() {
numberOfFlights++
println "flying"
}
void land() {
println "landing"
}
String getRegNumber() {
return regNumber
}
}
Plane plane = new Plane()
plane.fly()
plane.fly()
assert plane.getNumberOfFlights() == 2
println plane.getRegNumber()
B. Declarative programming.
In declarative programming one does not write instructions one by one but describes the desired end result instead.
1. Groovy based declarative syntax.
It is possible to create the syntax for custom declarative language and use it as needed in Groovy:
println "declarative programming"
def surroundedWithDashes(closure) {
println "-------------"
closure.call()
println "-------------"
}
def show(closure) {
println closure.call()
}
def sumOfNumbers = { a, b ->
return a + b
}
surroundedWithDashes {
show {
sumOfNumbers(3, 6)
}
show {
sumOfNumbers(1, 2)
}
show {
sumOfNumbers(5, 10)
}
}
Technically this example is basing on closures and from developer point of view, after the syntax is ready, it is enough to say surroundedWithDashes to get the layout accordingly.
2. Functional programming.
One can use Groovy in the context of functional programming paradigm as well which means using functions which accept some input and for the given input they always produce the same specific output without any side effects.
In Groovy, most results can be achieved by using just 3 functions:
collect, findAll and inject.
Here is the example with the first two:
println "functional programming"
def data = [1, 5, 8, 3, 5]
def result = data.collect { item -> item + 10 }
.findAll { item -> item > 13 }
.toUnique()
assert result == [15, 18]
The benefits come not only from function chaining but also from functional programming related capabilities like:
- memoization - caching the results of the function for the given inputs
- currying - creating functions from simple, generic ones
C. Metaprogramming.
This maybe should not be separate section in the context of programming paradigms but I really would like to make it stand out.
This is very important part of Groovy and it is kind of its hallmark.
1. Compile-time metaprogramming.
It means code generation during compile-time:
println "compile-time metaprogramming" // code generation during compile time
@Log
class Car2 {
def regNumber
def numberOfStarts = 0
def weight = 2
@NullCheck
Car2(regNumber) {
this.regNumber = regNumber
}
def start() {
numberOfStarts++
log.info "log: running"
}
def stop() {
log.info "log: stopping"
}
def getRegNumber() {
return regNumber
}
}
try {
new Car2().start()
}
catch (error) {
println "error was caught: ${error}"
}
new Car2("12345").start()
It is enough to decorate the class with @Log to get logging capabilities and with @NullCheck to make sure constructor parameter will not be null. So, there are no explicit null checks in the source code but only in the compiled code.
2. Runtime programming.
Intercepting, injecting and even synthesizing of methods can be done during Groovy runtime. This may be not so clear, so let me show you the example:
@Log
class Car3 {
def regNumber
def numberOfStarts = 0
def weight = 2
@NullCheck
Car3(regNumber) {
this.regNumber = regNumber
}
def start() {
numberOfStarts++
log.info "log: running"
}
def stop() {
log.info "log: stopping"
}
def getRegNumber() {
return regNumber
}
def methodMissing(String name, args) {
log.info "log: called missing method: ${name} with arguments: ${args}"
}
}
def car3 = new Car3("12345")
car3.someUnexistingMethod("with parameter")
car3.metaClass.additionalMethod = { param ->
log.info "log: called added method: additionalMethod with parameter: ${param}"
}
car3.additionalMethod("paramValue")
After special built-in method methodMissing is implemented, it will be called when unexisting method of the class is used. Also, additionalMethod can be added to the class and used in the runtime successfully.
Why to use such a feature at all you could ask? It is very useful when testing and when mocks are needed. I wrote related article in the past: mocking-with-groovy
It can also be used to more advanced tasks like writing testing frameworks.
I created one for Jenkins some time ago and so you may see the simplified version in Github as well: Jenkinson
Summary
These were very simple examples of various ways you can write code in Groovy. I really just touched the areas here but I did not want to make this post very long. I wish to make some people curious and interested about this topic.
I do not know many of the Groovy details either but I must say I personally used all of the above features to some extent in the past. There is obvious source of very detailed information there:
GROOVY-DOC.
If you have never seen the doc it is definitely worth it as there are tons of other language features to be used.
In my opinion, this flexibility is the Groovy advantage when considering the language for some task or project but also it is great feature when teaching programming.
I hope you find this short article useful.
Top comments (0)