Date post: | 15-Jul-2015 |
Category: |
Technology |
Upload: | ivan-lopez |
View: | 744 times |
Download: | 1 times |
AST and compilation
▷ Abstract Syntax Tree
▷ AST modified during compilation
▷ Hook into the compiler phases
AST transformations categories
▷ Code generation
▷ Class design
▷ Logging improvements
▷ Declarative concurrency
▷ Cloning and externalizing
▷ Safe scripting
▷ Compiler directives
▷ Dependencies handling
class User { String name Integer age}
def u = new User(name: 'Iván', age: 35)
println u // User@1d2a54b2
@groovy.transform.ToStringclass User { String name Integer age}
def u = new User(name: 'Iván', age: 35)assert u.toString() == 'User(Iván, 35)'
class User { String name Integer age}
def u = new User(name: 'Iván', age: 35)
println u // User@1d2a54b2
@groovy.transform.ToStringclass User { String name Integer age}
def u = new User(name: 'Iván', age: 35)assert u.toString() == 'User(Iván, 35)'
String toString() { def _result = new StringBuilder() _result.append('User(') _result.append(this.name) _result.append(', ') _result.append(this.age) _result.append(')') return _result.toString()}
class User { String name Integer age}
def u = new User(name: 'Iván', age: 35)
println u // User@1d2a54b2
@ToString
▷ includeNames, excludes, includes, includeSuper, includeSuperProperties, includeFields, ignoreNulls, includePackage, cache
@ToString
▷ includeNames, excludes, includes, includeSuper, includeSuperProperties, includeFields, ignoreNulls, includePackage, cache
@groovy.transform.ToString(includeNames = true, excludes = ['name'])class User { String name Integer age}
def u = new User(name: 'Iván', age: 35)assert u.toString() == 'User(age:35)'
@EqualsAndHashCode
▷ Generate equals and hashCode implementations
▷ Effective Java items 8 & [email protected] User { String name Integer age}
def u1 = new User(name: 'Iván', age: 35)def u2 = new User(name: 'Iván', age: 35)
assert u1 == u2assert u1.hashCode() == u2.hashCode()
int hashCode() { def _result = HashCodeHelper.initHash() _result = HashCodeHelper.updateHash(_result, this.name) _result = HashCodeHelper.updateHash(_result, this.age) return _result}
boolean canEqual(Object other) { return other instanceof User}
boolean equals(Object other) { if (other == null) { return false } if (this.is(other)) { return true } if (!(other instanceof User)) { return false } User otherTyped = ((other) as User) if (!(otherTyped.canEqual(this))) { return false } if (!(this.getName().is(otherTyped.getName()))) { if (this.getName().is(this) && !(otherTyped.getName().is(otherTyped)) || !(this.getName().is(this)) && otherTyped.getName().is(otherTyped)) { return false } else { if (!(this.getName().is(this) && otherTyped.getName().is(otherTyped))) { if (!(this.getName() == otherTyped.getName())) { return false } } } } if (!(this.getAge().is(otherTyped.getAge()))) { if (this.getAge().is(this) && !(otherTyped.getAge().is(otherTyped)) || !(this.getAge().is(this)) && otherTyped.getAge().is(otherTyped)) { return false } else { if (!(this.getAge().is(this) && otherTyped.getAge().is(otherTyped))) { if (!(this.getAge() == otherTyped.getAge())) { return false } } } } return true}
@EqualsAndHashCode
▷ excludes, includes, callSuper, includeFields, cache, useCanEqual
@groovy.transform.EqualsAndHashCode(includes = 'name')class User { String name Integer age}
def u1 = new User(name: 'Iván', age: 35)def u2 = new User(name: 'Iván', age: 42)
assert u1 == u2assert u1.hashCode() == u2.hashCode()
@TupleConstructor
▷ Generate constructors
@groovy.transform.TupleConstructorclass User { String name Integer age}
@TupleConstructor
▷ Generate constructors
@groovy.transform.TupleConstructorclass User { String name Integer age}
// Default map constructordef u1 = new User(name: 'Iván', age: 35)
@TupleConstructor
▷ Generate constructors
@groovy.transform.TupleConstructorclass User { String name Integer age}
// Default map constructordef u1 = new User(name: 'Iván', age: 35)
// Generated tuple constructordef u2 = new User('Iván', 35)def u3 = new User('Iván')
@TupleConstructor
▷ Generate constructors
@groovy.transform.TupleConstructorclass User { String name Integer age}
// Default map constructordef u1 = new User(name: 'Iván', age: 35)
// Generated tuple constructordef u2 = new User('Iván', 35)def u3 = new User('Iván')
User(String name = null, Integer age = null) { this.name = name this.age = age }
@TupleConstructor
▷ excludes, includes, includeFields, includeProperties, includeSuperFields, includeSuperProperties, callSuper, force
@Canonical
▷ @ToString + @EqualsAndHashCode + @TupleConstructor
@groovy.transform.Canonicalclass User { String name Integer age}
@Canonical
▷ @ToString + @EqualsAndHashCode + @TupleConstructor
def u1 = new User(name: 'Iván', age: 35)assert u1.toString() == 'User(Iván, 35)' // @ToString
@groovy.transform.Canonicalclass User { String name Integer age}
@Canonical
▷ @ToString + @EqualsAndHashCode + @TupleConstructor
def u2 = new User('Iván', 35) // @TupleConstructorassert u2.toString() == 'User(Iván, 35)'
def u1 = new User(name: 'Iván', age: 35)assert u1.toString() == 'User(Iván, 35)' // @ToString
@groovy.transform.Canonicalclass User { String name Integer age}
@Canonical
▷ @ToString + @EqualsAndHashCode + @TupleConstructor
assert u1 == u2 // @EqualsAndHashCodeassert u1.hashCode() == u2.hashCode() // @EqualsAndHashCode
def u2 = new User('Iván', 35) // @TupleConstructorassert u2.toString() == 'User(Iván, 35)'
def u1 = new User(name: 'Iván', age: 35)assert u1.toString() == 'User(Iván, 35)' // @ToString
@groovy.transform.Canonicalclass User { String name Integer age}
@InheritConstructors
▷ Reduce boilerplate code when parent classes have multiple constructors
▷ Useful when overriding exception classes
protected MyException(String param0, Throwable param1, boolean param2, boolean param3) { super(param0, param1, param2, param3)}
public MyException(Throwable param0) { super(param0) }
public MyException(String param0, Throwable param1) { super(param0, param1) }
public MyException(String param0) { super(param0) }
public MyException() { super() }
@groovy.transform.InheritConstructorsclass MyException extends Exception {}
@Lazy
▷ Lazy initialization of fields
▷ Useful when creating expensive resources
▷ Effective Java item 71
class SomeBean { @Lazy LinkedList myField}
public LinkedList getMyField() { if ($myField != null) { $myField } else { $myField = new LinkedList() }}
@Sortable
▷ Comparable interface
▷ compareTo method natural order
▷ N methods returning comparators
▷ Effective Java item 12
public int compareTo(User other) { if (this.is(other)) return 0 Integer value = 0 value = this.name <=> other.name if (value != 0) return value value = this.age <=> other.age if (value != 0) return value value = this.born <=> other.born if (value != 0) return value return 0}
private static class User$NameComparator extends AbstractComparator<User> { public int compare(User arg0, User arg1) { if (arg0 == arg1) return 0 if (arg0 != null && arg1 == null) return -1 if (arg0 == null && arg1 != null) return 1 return arg0.name <=> arg1.name }}
private static class User$AgeComparator extends AbstractComparator<User> { ...}
@groovy.transform.Sortableclass User { String name Integer age Integer born}
def users = [ new User(name: 'Mary', age: 15, born: 2000), new User(name: 'Peter', age: 44, born: 1970), new User(name: 'John', age: 35, born: 1979),]
@groovy.transform.Sortableclass User { String name Integer age Integer born}
assert users.sort(false, User.comparatorByName())*.name == ['John', 'Mary', 'Peter']assert users.sort(false, User.comparatorByAge())*.born == [2000, 1979, 1970]
def users = [ new User(name: 'Mary', age: 15, born: 2000), new User(name: 'Peter', age: 44, born: 1970), new User(name: 'John', age: 35, born: 1979),]
@groovy.transform.Sortableclass User { String name Integer age Integer born}
@Sortable
▷ includes, excludes
@groovy.transform.Sortable(excludes = 'age')class User { String name Integer age Integer born}
def users = [ new User(name: 'Mary', age: 15, born: 2000), new User(name: 'Peter', age: 44, born: 1970), new User(name: 'John', age: 35, born: 1979),]
assert users.sort(false, User.comparatorByName())*.name == ['John', 'Mary', 'Peter']assert users.sort(false, User.comparatorByAge())*.born == [2000, 1979, 1970]
@Builder
▷ Create fluent API calls
▷ Multiple building strategies
▷ Multiple configuration options: builder name, prefix, excludes, includes,...
▷ Effective Java item 2
@groovy.transform.builder.Builderclass User { String name Integer age Integer born}
def u = User.builder() .name('Iván') .age(35) .born(1979) .build()
assert u.name == 'Iván'assert u.age == 35assert u.born == 1979
public static class User$UserBuilder extends Object {
private String name private Integer age private Integer born
public User$UserBuilder() { }
public User$UserBuilder name(String name) { this.name = name return this }
public User$UserBuilder age(Integer age) { this.age = age return this }
public User$UserBuilder born(Integer born) { this.born = born return this }
public User build() { User _theUser = new User() _theUser.name = name _theUser.age = age _theUser.born = born return _theUser }}
@groovy.transform.builder.Builderclass User { String name Integer age Integer born}
def u = User.builder() .name('Iván') .age(35) .born(1979) .build()
assert u.name == 'Iván'assert u.age == 35assert u.born == 1979
@Delegate
▷ Implements delegation design pattern
▷ Delegate calls on object to method on delegated properties
▷ All public methods are delegated
import java.time.LocalDate
class Conference { @groovy.lang.Delegate LocalDate when String name}
def greach = new Conference(name: 'Greach', when: LocalDate.of(2015, 04, 10))def gr8conf = new Conference(name: 'GR8Conf' when: LocalDate.of(2015, 06, 02))
def greach = new Conference(name: 'Greach', when: LocalDate.of(2015, 04, 10))def gr8conf = new Conference(name: 'GR8Conf' when: LocalDate.of(2015, 06, 02))
assert greach.isBefore(gr8conf)
import java.time.LocalDate
class Conference { @groovy.lang.Delegate LocalDate when String name}
class Conference { ... public boolean isAfter(ChronoLocalDate param0) { when.isAfter(param0) }
public boolean isBefore(ChronoLocalDate param0) { when.isBefore(param0) } ...}
def greach = new Conference(name: 'Greach', when: LocalDate.of(2015, 04, 10))def gr8conf = new Conference(name: 'GR8Conf' when: LocalDate.of(2015, 06, 02))
assert greach.isBefore(gr8conf)
import java.time.LocalDate
class Conference { @groovy.lang.Delegate LocalDate when String name}
@groovy.transform.Immutableclass User { String name Integer age}
def u = new User(name: 'Iván', age: 35)
// This does not compile// You are not allowed to overwrite// the final class 'User'.class Admin extends User {}
@groovy.transform.Immutableclass User { String name Integer age}
def u = new User(name: 'Iván', age: 35)
@groovy.transform.Immutableclass User { String name Integer age}
def u = new User(name: 'Iván', age: 35)
try { u.name = 'John'} catch (ReadOnlyPropertyException e) { println e}
// This does not compile// You are not allowed to overwrite // the final class 'User'.class Admin extends User {}
@Memoized
▷ Cache the result of a method
@groovy.transform.MemoizedLong fibonacci(Integer n) { if (n < 2) return 1 else return fibonacci(n-1) + fibonacci(n-2)}
fibonacci(300)
@Memoized
▷ Cache the result of a method
@groovy.transform.MemoizedLong fibonacci(Integer n) { if (n < 2) return 1 else return fibonacci(n-1) + fibonacci(n-2)}
fibonacci(300)
@groovy.transform.MemoizedUser getUserInfo(Long userId) { // Expensive repetitive // network operation}
@Log, @Log4j, @Log4j2, @Slf4j
@groovy.util.logging.Log4jclass MyClass { void method() { log.debug "My debug message" }}
▷ Static final field for the logger
@Grab
▷ Grape dependency manager
@Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate
// or
@Grab('org.springframework:spring-orm:3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate
@GrabResolver
▷ Grape dependency manager
@Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate
// or
@Grab('org.springframework:spring-orm:3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate
@GrabResolver(name='restlet', root='http://maven.restlet.org/')@Grab(group='org.restlet', module='org.restlet', version='1.1.6')
@GrabExclude
▷ Grape dependency manager
@Grab(group='org.springframework', module='spring-orm', version='3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate
// or
@Grab('org.springframework:spring-orm:3.2.5.RELEASE')import org.springframework.jdbc.core.JdbcTemplate
@GrabResolver(name='restlet', root='http://maven.restlet.org/')@Grab(group='org.restlet', module='org.restlet', version='1.1.6')
@Grab('net.sourceforge.htmlunit:htmlunit:2.8')@GrabExclude('xml-apis:xml-apis')
Thanks!Any questions?
@ilopmar
https://github.com/lmivan
Iván López
http://kcy.me/1zr7q