19. @IMMUTABLE
No poder modificar los campos de una clase
Si los campos son otras clases…
Comprueba que sean immutables tambien
O que esten en una lista blanca de clases
7 . 5
24. @IMMUTABLE (DI FRIENDLY - III)
Annotated class
Google Guice Module
package madridgug.immutable
@ImmutableDI
class SecurityConfig2 {
URI provider
String key
}
def ctor = SecurityConfig2.getConstructor(URI, String)
bind(SecurityConfig2)
.toConstructor(ctor)
.in(Scopes.SINGLETON)
7 . 10
25. @IMMUTABLE (DI FRIENDLY - IV)
given: 'a Google Guice Injector'
Injector di = Guice.createInjector(new GuiceModule())
when: 'getting the configuration instance'
SecurityConfig2 config = di.getInstance(SecurityConfig2)
then: 'properties should've been populated properly'
config.key == 'key'
config.provider == URI.create('https://nowhereprovider.io')
when: 'trying to change properties again'
config.key = 'another key'
then: 'an error will be thrown'
thrown(ReadOnlyPropertyException)
7 . 11
26. @IMMUTABLE (OPTIONAL)
Considerado immutable tambien
package madridgug.immutable
import groovy.transform.Immutable
@Immutable
class Person {
String name
Integer age
Optional<String> mobile
}
7 . 12
28. @AUTOIMPLEMENT
package madridgug.autoimplement
interface MessageSender {
/**
* Sends a message {@link String}
*/
UUID sendMessage(String message)
/**
* Checks whether the message has been received yet or not
*/
boolean isMessageReceived(UUID messageId)
/**
* Checks whether the message has been sent yet or not
*/
Boolean isMessageSent(UUID messageId)
}
7 . 14
35. @DELEGATE ENHANCEMENTS
Ahora @Delegate se puede usar en getters
package madridgug.delegate
class TennisPlayer {
List<Map> wins
String name
@Delegate
Integer getWinSize() {
return wins.size()
}
}
7 . 21
36. @DELEGATE ENHANCEMENTS
given: 'two players'
def good = new TennisPlayer(name: 'John', wins: [
[name: 'Roland Garros 2014'],
[name: 'Roland Garros 2015'],
[name: 'Roland Garros 2016']
])
def bad = new TennisPlayer(name: 'Peter', wins: [
[name: 'Somewhere 2002']
])
and: 'the good one should be greater than the bad one'
good.intValue() > bad.intValue()
7 . 22
37. @NAMEDVARIANT
Posibilidad de llamar a un metodo con la instancia del
tipo requerido o …
Descomponiendo la instancia en sus diferentes attributos
7 . 23
38. @NAMEDVARIANT
package madridgug.namedvariant
class Car {
String brand
String model
Integer hp
}
package madridgug.namedvariant
import groovy.util.logging.Slf4j
import groovy.transform.NamedVariant
import groovy.transform.NamedDelegate
@Slf4j
class CarProcessor {
@NamedVariant
void process(@NamedDelegate Car car) {
log.info "processing: ${car.model}"
}
}
7 . 24
39. @NAMEDVARIANT
Pasando una instancia
Car car = new Car(brand: 'ferrari', model: 'F450', hp: 450)
CarProcessor processor = new CarProcessor()
when: 'processing the car'
processor.process(car)
7 . 25
40. @NAMEDVARIANT
Pasando propiedades
and: 'a processor instance'
CarProcessor processor = new CarProcessor()
when: 'processing the car'
processor.process(brand: 'ferrari', model: 'F450', hp: 450)
7 . 26
43. @GENERATED
Concebido para los autores de transformciones AST
Ayuda a las herramientas de cobertura de codigo
Para saber que codigo es generado y cual no
Hasta ahora ni JaCoCo ni Cobertura lo tenian facil
7 . 29
45. LAS UTILIDADES DE MACROS
ABSTRAEN…
La generacion de nuevo codigo
El filtrado de codigo sobre el que quieres aplicar una
transformacion
La transformacion de codigo
8 . 2
48. MACRO METHOD
Este codigo
Deberia contemplar este test
package madridgug.macros.method
class SafeCalls {
@MakeParamSafe
static Integer inc(Integer a) {
// if (!a) { return 1 }
return a + 1
}
}
void 'check unsafe calls'() {
expect: 'unsafe calls to return ok'
SafeCalls.inc(null) == 1
}
8 . 5
49. MACRO METHOD
Como crear la guarda antes del codigo ?
API AST "A pelo"
GeneralUtils para reducirlo
macro {}
8 . 6
50. MACRO METHOD
API ASTs
GeneralUtils
IfStatement safeGuard = new IfStatement(
new NotExpression(
new VariableExpression(paramName)
),
new ReturnStatement(new ConstantExpression(1)),
new EmptyStatement()
)
IfStatement safeGuard = ifS(
notX(varX(paramName)),
returnS(constX(1))
) as IfStatement
8 . 7
51. MACRO METHOD
macro {}
1 Las expressiones se interpolan
2 Dentro de codigo plano
VariableExpression paramRef = Utils.getParamAsVarX(methodNode) (1)
IfStatement safeGuard = macro {
if (! $v { paramRef }) { (2)
return 1
}
}
8 . 8
52. MACRO METHOD
Mejor caso de uso
BlockStatement getMD5Code(final String propertyName) {
return macro(true) {
java.security.MessageDigest.getInstance('MD5')
.digest(${propertyName}.getBytes('UTF-8'))
.encodeHex()
.toString()
}
}
8 . 9
69. LAMBDAS + CLOSURES + M. REF.
Todo junto
static Integer applyBoth() {
return Stream
.of(1, 2, 3)
.filter { y -> y % 2 == 0 } // closure syntax
.map( y -> y + 1 ) // lambda syntax
.mapToInt(Integer::intValue) // method ref
.sum()
}
9 . 7
70. LAMBDAS OTHERS
// Sin parentesis
static Function<Integer, Integer> inc = x -> x + 1
// Con valor por defecto (No existe en Java)
static Function<Integer, Integer> dob = (x = 0) -> x * 2
// Con parentesis
static Function<Integer, Integer> tri = (Integer x) -> x * 3
// Con llaves
static Function<Integer, Integer> com = (Integer x) -> {
x + 4
}
9 . 8
71. LAMBDAS OTHERS
Son las lambdas realmente lambdas ?
Con @CompileStatic ⇒ lambdas
Sin @CompileStatic ⇒ closures
9 . 9
72. LOOPS
En general todos los tipos de loops que se puedan hacer
en Java
Ahora se puede hacer un do / while por ejemplo
9 . 10
75. IDENTIDAD
package madridgug.parrot
import groovy.transform.Immutable
class Identity {
@Immutable
class Person {
String name
}
void checkIdentity(Person left, Person right) {
if (left === right) { (1)
log.info "es la misma persona"
}
if (left == right) { (2)
log.info "son personas con caracteristicas iguales"
}
9 . 13