Introdução ao Groovy Language
1. Visão geral
Groovy is a dynamic, scripting language for the JVM. Ele é compilado no bytecode e combina perfeitamente com o código e as bibliotecas Java.
Neste artigo, vamos dar uma olhada em alguns dos recursos essenciais deGroovy, incluindo sintaxe básica, estruturas de controle e coleções.
Em seguida, examinaremos alguns dos principais recursos que o tornam uma linguagem atraente, incluindo segurança nula, verdade implícita, operadores e seqüências de caracteres.
2. Meio Ambiente
3. Funcionalidades básicas
Existem muitos recursos úteis no Groovy. Agora, vamos dar uma olhada nos blocos de construção básicos da linguagem e como ela difere do Java.
Agora, vamos dar uma olhada nos blocos de construção básicos da linguagem e como ela difere do Java.
3.1. Digitação dinâmica
Um dos recursos mais importantes do Groovy é seu suporte para tipagem dinâmica.
As definições de tipo são opcionais e os tipos reais são determinados em tempo de execução. Vamos dar uma olhada nessas duas classes:
class Duck {
String getName() {
'Duck'
}
}
class Cat {
String getName() {
'Cat'
}
}
Essas duas classes definem o mesmo métodogetName, mas não é definido explicitamente em um contrato.
Agora, imagine que temos uma lista de objetos contendo patos e gatos que possuem o métodogetName. Com o Groovy, podemos fazer o seguinte:
Duck duck = new Duck()
Cat cat = new Cat()
def list = [duck, cat]
list.each { obj ->
println obj.getName()
}
O código será compilado e a saída do código acima seria:
Duck
Cat
3.2. Conversão verdadeira implícita
Como no JavaScript, o Groovy avalia cada objeto como um booleano, se necessário, por exemplo. ao usá-lo dentro de uma instruçãoif ou ao negar o valor:
if("hello") {...}
if(15) {...}
if(someObject) {...}
Existem algumas regras simples a serem lembradas sobre essa conversão:
-
MatrizesCollections, não vazias, mapas avaliados emtrue
-
Matcher com pelo menos uma correspondência avalia paratrue
-
Iterators eEnumerations com outros elementos são forçados atrue
-
Strings não vazio,GStrings eCharSequences, são forçados atrue
-
Números diferentes de zero são avaliados emtrue
-
Referências de objetos não nulos são forçadas atrue
Se quisermos personalizar a conversão de verdade implícita, podemos definir nosso métodoasBoolean().
3.3. Importações
Alguns pacotes são importados por padrão e não precisamos importá-los explicitamente:
import java.lang.*
import java.util.*
import java.io.*
import java.net.*
import groovy.lang.*
import groovy.util.*
import java.math.BigInteger
import java.math.BigDecimal
4. Transformações AST
As transformações AST (Abstract Syntax Tree) nos permitem entrar no processo de compilação do Groovy e personalizá-lo para atender às nossas necessidades. Isso é feito no momento da compilação, portanto, não há penalidade no desempenho ao executar o aplicativo. Podemos criar nossas transformações AST, mas também podemos usar as incorporadas.
Podemos criar nossas transformações, ou podemos nos beneficiar das incorporadas.
Vamos dar uma olhada em algumas anotações que vale a pena conhecer.
4.1. AnotaçãoTypeChecked
Essa anotação é usada para forçar o compilador a executar uma verificação estrita de tipo para partes de código anotadas. O mecanismo de verificação de tipo é extensível, portanto podemos fornecer uma verificação de tipo ainda mais rigorosa do que a disponível em Java, quando desejado.
Vamos dar uma olhada no exemplo abaixo:
class Universe {
@TypeChecked
int answer() { "forty two" }
}
Se tentarmos compilar este código, observaremos o seguinte erro:
[Static type checking] - Cannot return value of type java.lang.String on method returning type int
A anotação@TypeChecked pode ser aplicada a classes e métodos.
4.2. AnotaçãoCompileStatic
Essa anotação permite que o compilador execute verificações em tempo de compilação, como é feito com o código Java. Depois disso, o compilador executa uma compilação estática, ignorando o protocolo de metaobjeto do Groovy.
Quando uma classe é anotada, todos os métodos, propriedades, arquivos, classes internas, etc. da classe anotada será verificada. Quando um método é anotado, a compilação estática é aplicada apenas aos itens (fechamentos e classes internas anônimas) que são delimitados por esse método.
5. Propriedades
No Groovy, podemos criar POGOs (Plain Old Groovy Objects) que funcionam da mesma maneira que POJOs em Java, embora sejam mais compactos porquegetters and setters are automatically generated for public properties durante a compilação. É importante lembrar que eles serão gerados apenas se ainda não estiverem definidos.
Isso nos dá a flexibilidade de definir atributos como campos abertos, mantendo a capacidade de substituir o comportamento ao definir ou obter os valores.
Considere este objeto:
class Person {
String name
String lastName
}
Como o escopo padrão para classes, campos e métodos épublic –, esta é uma classe pública e os dois campos são públicos.
O compilador irá convertê-los em campos privados e adicionar os métodosgetName(),setName(),getLastName()esetLasfName(). Se definirmossetteregetter para um campo específico, o compilador não criará um método público.
5.1. Notações de atalho
O Groovy oferece uma notação de atalho para obter e definir propriedades. Em vez da maneira Java de chamar getters e setters, podemos usar uma notação de acesso do tipo campo:
resourceGroup.getResourcePrototype().getName() == SERVER_TYPE_NAME
resourceGroup.resourcePrototype.name == SERVER_TYPE_NAME
resourcePrototype.setName("something")
resourcePrototype.name = "something"
6. Operadores
Vamos agora dar uma olhada nos novos operadores adicionados aos conhecidos do Java puro.
6.1. Desreferência nula-segura
O mais popular é o operador de desreferência de segurança nula“?” que nos permite evitarNullPointerException ao chamar um método ou acessar uma propriedade de um objetonull. É especialmente útil em chamadas encadeadas onde um valornull pode ocorrer em algum ponto da cadeia.
Por exemplo, podemos chamar com segurança:
String name = person?.organization?.parent?.name
No exemplo acima, se aperson,person.organization ouorganization.parent sãonull, entãonull é retornado.
6.2. Operador Elvis
O operador Elvis“?: ”permite condensar expressões ternárias. Estes dois são equivalentes:
String name = person.name ?: defaultName
and
String name = person.name ? person.name : defaultName
Ambos atribuem o valor deperson.name à variável de nome se forGroovy true (neste caso, nãonulle tem um comprimento denon-zero).
6.3. Operador de nave espacial
O operador de nave espacial“<⇒” é um operador relacional com desempenho semelhante aocompareTo() de Java, que compara dois objetos e retorna -1, 0 ou +1, dependendo dos valores de ambos os argumentos.
Se o argumento esquerdo for maior que o direito, o operador retornará 1. Se o argumento da esquerda for menor que o da direita, o operador retornará -1. Se os argumentos forem iguais, 0 será retornado.
A maior vantagem de usar os operadores de comparação é o manuseio suave denulls, de modo quex <⇒ y nunca jogará umNullPointerException:
println 5 <=> null
O exemplo acima imprimirá 1 como resultado.
7. Cordas
Existem várias maneiras de expressar literais de string. A abordagem usada em Java (cadeias de caracteres entre aspas duplas) é suportada, mas também é permitido o uso de aspas simples quando preferencial.
Sequências de várias linhas, às vezes chamadas de heredocs em outros idiomas, também são suportadas, usando aspas triplas (simples ou duplas).
Sequências de várias linhas, às vezes chamadas de heredocs em outros idiomas, também são suportadas, usando aspas triplas (simples ou duplas).
Strings definidas com aspas duplas suportam interpolação usando a sintaxe$\{}:
def name = "Bill Gates"
def greeting = "Hello, ${name}"
Na verdade, qualquer expressão pode ser colocada dentro de$\{}:
def name = "Bill Gates"
def greeting = "Hello, ${name.toUpperCase()}"
Uma String com aspas duplas é chamada de GString se contiver uma expressão$\{}, caso contrário, é um objetoString simples.
O código abaixo será executado sem falhar no teste:
def a = "hello"
assert a.class.name == 'java.lang.String'
def b = 'hello'
assert b.class.name == 'java.lang.String'
def c = "${b}"
assert c.class.name == 'org.codehaus.groovy.runtime.GStringImpl'
8. Coleções e mapas
Vamos dar uma olhada em como algumas estruturas básicas de dados são tratadas.
8.1. Lists
Aqui está um código para adicionar alguns elementos a uma nova instância deArrayList em Java:
List list = new ArrayList<>();
list.add("Hello");
list.add("World");
E aqui está a mesma operação no Groovy:
List list = ['Hello', 'World']
As listas são, por padrão, do tipojava.util.ArrayList e também podem ser declaradas explicitamente chamando o construtor correspondente.
Não há uma sintaxe separada para aSet, mas podemos usar o tipocoercion para isso. Ou use:
Set greeting = ['Hello', 'World']
or:
def greeting = ['Hello', 'World'] as Set
8.2. Map
A sintaxe para aMap é semelhante, embora um pouco mais prolixa, porque precisamos ser capazes de especificar chaves e valores delimitados por dois pontos:
def key = 'Key3'
def aMap = [
'Key1': 'Value 1',
Key2: 'Value 2',
(key): 'Another value'
]
Após esta inicialização, obteremos um novoLinkedHashMap com as entradas:Key1 → Value1, Key2 → Value 2, Key3 → Another Value.
Podemos acessar as entradas no mapa de várias maneiras:
println aMap['Key1']
println aMap[key]
println aMap.Key1
9. Estruturas de controle
9.1. Condicionais:if-else
Groovy suporta a sintaxe condicionalif/else conforme o esperado:
if (...) {
// ...
} else if (...) {
// ...
} else {
// ...
}
9.2. Condicionais:switch-case
A instruçãoswitch é compatível com versões anteriores do código Java para que possamos cair em casos que compartilham o mesmo código para várias correspondências.
A diferença mais importante é que umswitch pode realizar a correspondência com vários tipos de valor diferentes:
def x = 1.23
def result = ""
switch ( x ) {
case "foo":
result = "found foo"
break
case "bar":
result += "bar"
break
case [4, 5, 6, 'inList']:
result = "list"
break
case 12..30:
result = "range"
break
case Number:
result = "number"
break
case ~/fo*/:
result = "foo regex"
break
case { it < 0 }: // or { x < 0 }
result = "negative"
break
default:
result = "default"
}
println(result)
O exemplo acima imprimiránumber.
9.3. Loops:while
Groovy suporta os loopswhile usuais como o Java:
def x = 0
def y = 5
while ( y-- > 0 ) {
x++
}
9.4. Loops:for
Groovy adota essa simplicidade e incentiva fortemente os loopsfor seguindo esta estrutura:
for (variable in iterable) { body }
O loopfor itera emiterable. Os iteráveis usados com freqüência são intervalos, coleções, mapas, matrizes, iteradores e enumerações. De fato, qualquer objeto pode ser iterável.
As chaves ao redor do corpo são opcionais se consistirem em apenas uma declaração. Abaixo estão exemplos de iteração emrange,list,array,map estrings:
def x = 0
for ( i in 0..9 ) {
x += i
}
x = 0
for ( i in [0, 1, 2, 3, 4] ) {
x += i
}
def array = (0..4).toArray()
x = 0
for ( i in array ) {
x += i
}
def map = ['abc':1, 'def':2, 'xyz':3]
x = 0
for ( e in map ) {
x += e.value
}
x = 0
for ( v in map.values() ) {
x += v
}
def text = "abc"
def list = []
for (c in text) {
list.add(c)
}
A iteração do objeto torna o saveiro Groovyfor-uma estrutura de controle sofisticada. É uma contrapartida válida ao uso de métodos que iteram sobre um objeto com fechamentos, como usar o métodoCollection’s each.
A principal diferença é que o corpo de um loopfor não é um fechamento, isso significa que este corpo é um bloco:
for (x in 0..9) { println x }
considerando que este organismo é um encerramento:
(0..9).each { println it }
Mesmo parecendo semelhantes, eles são muito diferentes na construção.
Um fechamento é um objeto próprio e possui recursos diferentes. Ele pode ser construído em um local diferente e passado para o métodoeach. No entanto, o corpo do saveirofor-é gerado diretamente comobytecode em seu ponto de aparecimento. Nenhuma regra de escopo especial se aplica.
10. Manipulação de exceção
A grande diferença é que o tratamento de exceções verificadas não é imposto.
Para lidar com exceções gerais, podemos colocar o código potencialmente causador de exceções em um blocotry/catch:
try {
someActionThatWillThrowAnException()
} catch (e)
// log the error message, and/or handle in some way
}
Ao não declarar o tipo de exceção que capturamos, qualquer exceção será capturada aqui.
11. Encerramentos
Simplificando, um fechamento é um bloco anônimo de código executável que pode ser passado para variáveis e tem acesso aos dados no contexto em que foi definido.
Eles também são semelhantes às classes internas anônimas, embora não implementem uma interface ou estendam uma classe base. Eles são semelhantes aos lambdas em Java.
Curiosamente, o Groovy pode tirar o máximo proveito das adições do JDK que foram introduzidas para oferecer suporte a lambdas, especialmente a API de streaming. Sempre podemos usar closures onde expressões lambda são esperadas.
Vamos considerar o exemplo abaixo:
def helloWorld = {
println "Hello World"
}
A variávelhelloWorld agora contém uma referência para o fechamento, e podemos executá-la chamando seu métodocall:
helloWorld.call()
O Groovy nos permite usar uma sintaxe de chamada de método mais natural - ele invoca o métodocall para nós:
helloWorld()
11.1. Parâmetros
Como os métodos, os fechamentos podem ter parâmetros. Existem três variantes.
No último exemplo, como não há nada declpersistence_startared, há apenas um parâmetro com o nome padrãoit. O fechamento modificado que imprime o que é enviado seria:
def printTheParam = { println it }
Poderíamos chamar assim:
printTheParam('hello')
printTheParam 'hello'
Também podemos esperar parâmetros nos fechamentos e passá-los ao chamar:
def power = { int x, int y ->
return Math.pow(x, y)
}
println power(2, 3)
A definição de tipo de parâmetros é igual às variáveis. Se definirmos um tipo, podemos apenas usá-lo, mas também podemos e passamos o que quisermos:
def say = { what ->
println what
}
say "Hello World"
11.2. Retorno Opcional
A última declaração de um fechamento pode ser implicitamente retornada sem a necessidade de escrever uma declaração de retorno. Isso pode ser usado para reduzir ao mínimo o código padrão. Assim, um fechamento que calcula o quadrado de um número pode ser reduzido da seguinte forma:
def square = { it * it }
println square(4)
Este encerramento usa o parâmetro implícitoite a instrução de retorno opcional.
12. Conclusão
Este artigo forneceu uma introdução rápida ao idioma Groovy e seus principais recursos. Começamos introduzindo conceitos simples, como sintaxe básica, instruções condicionais e operadores. Também demonstramos alguns recursos mais avançados, como operadores e fechamentos.
Se quiser encontrar mais informações sobre o idioma e sua semântica, você pode ir diretamente paraofficial site.