rethinking api design with traits
DESCRIPTION
Groovy 2.3 introduces the concept of traits in the language. Traits look like interfaces, but allow the developer to add both implementation and state into it. It introduces multiple inheritance in the language while avoiding the diamond problem. Traits will let you rethink the way you design APIs in Groovy, by favoriting composition of behaviors.TRANSCRIPT
![Page 1: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/1.jpg)
© 2014 SpringOne 2GX. All rights reserved. Do not distribute without permission.
Rethinking API design with traits
By Cédric Champeau
![Page 2: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/2.jpg)
#s2gx #groovylang @CedricChampeau
About me
Pivotal employee
Core Groovy committerCompilation configuration
Static type checking & Static compilation
Traits, new template engine, ...
Groovy in Action 2 co-author
Misc OSS contribs (Gradle plugins, deck2pdf, jlangdetect, ...)
2
![Page 3: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/3.jpg)
#s2gx #groovylang @CedricChampeau
Social (or why to use #groovylang)
3
![Page 4: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/4.jpg)
#s2gx #groovylang @CedricChampeau
Groovy is Open-Source
• Licensed under APL2• 100+ contributors• 10000+ commits• 1.7+ million downloads in 2013• On GitHub since summer 2011• Dependency of 25000+ OSS projects
4
![Page 5: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/5.jpg)
Life without traits(or why it's primarily a static issue)
5
![Page 6: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/6.jpg)
#s2gx #groovylang @CedricChampeau
Life without traits, pre Java 8
• Contracts are defined through interfaces• Interfaces are mandatory in a static type system• Base behavior defined through abstract classes• To implement multiple contracts, you need multiple abstract
classes• … or you can use duck typing in Groovy
6
![Page 7: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/7.jpg)
#s2gx #groovylang @CedricChampeau
Life without traits, pre Java 8
• Even in Groovy...• Problems with inheritance
• Hard to determine whether a class explicitly or implicitly implements a contract
7
![Page 8: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/8.jpg)
#s2gx #groovylang @CedricChampeau
@Mixin
• Deprecated in Groovy 2.3• Combines static and dynamic features to achieve runtime mixins
8
class Cat { void meow() { println 'Meow!' }}@Mixin(Cat)class YoutubeDog { void bark() { println 'Woof!' }}def dog = new YoutubeDog()dog.meow()dog.bark()
![Page 9: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/9.jpg)
#s2gx #groovylang @CedricChampeau
@Mixin
• Mixed in class is not an instance of the mixin• Problems with inheritance• Memory• Difference between the mixed instance and the mixin instance• Buggy (really)
9
![Page 10: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/10.jpg)
#s2gx #groovylang @CedricChampeau
Delegation
• Delegation vs inheritance• Delegation keeps concerns separate
• Cleaner responsibility model
• Not always suitable
• Foo “is a Bar” → inheritance
• Foo “acts as a Bar” → composition
• Optionally requires interfaces
10
![Page 11: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/11.jpg)
#s2gx #groovylang @CedricChampeau
@Delegate : composition model
11
/* An API-limited version of a Stack */class StackOnly<T> { Stack<T> delegate = new Stack<>()
void push(T e) { delegate.push(e) } T pop() { delegate.pop() }}
def t = new StackOnly<String>()t.push 'a't.push 'b'
assert t.pop() == 'b'
![Page 12: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/12.jpg)
#s2gx #groovylang @CedricChampeau
@Delegate : composition model
12
/* An API-limited version of a Stack */class StackOnly<T> { @Delegate(interfaces=false, includes=['push','pop']) Stack<T> delegate = new Stack<>()}
def t = new StackOnly<String>()t.push 'a't.push 'b'
assert t.pop() == 'b'assert t.get(0) == 'a' // will fail because get is not delegated
![Page 13: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/13.jpg)
#s2gx #groovylang @CedricChampeau
@Delegate : composition model
• Excellent fit when the delegating object requires a subset of the features of the delegate
• Delegation + interfaces used as the poor man's multiple inheritance solution
13
![Page 14: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/14.jpg)
#s2gx #groovylang @CedricChampeau
Groovy specific : Extension methods
• URL#getText(String encoding)• Allows adding behavior to existing classes• Supported by @CompileStatic• Add your own thanks to extension modules
– See http://docs.groovy-lang.org/latest/html/documentation/#_extension_modules
• Not visible from Java
14
![Page 15: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/15.jpg)
#s2gx #groovylang @CedricChampeau
Java 8 default methods
• Some kind of traits• An interface
• With default implementation
15
public interface Greeter { default void greet() { System.out.println("Hello!"); }}
// Usable from Groovyclass DefaultGreeter implements Greeter {}
![Page 16: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/16.jpg)
#s2gx #groovylang @CedricChampeau
Java 8 default methods
• Stateless• No state = No diamond issue
• Virtual
16
class VirtualGreeter implements Greeter { @Override void greet() { println "Virtual Hello!" }}
● Backward compatible● Groovy does not support writing default methods
![Page 17: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/17.jpg)
Traits in Groovy
17
![Page 18: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/18.jpg)
#s2gx #groovylang @CedricChampeau
Traits
• Similar to Java 8 default methods• Supported in JDK 6, 7 and 8• Stateful• Composable• Not really virtual...
18
![Page 19: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/19.jpg)
#s2gx #groovylang @CedricChampeau
Traits : the basics
• Declared through the “trait” new keyword• Requires Groovy 2.3+
19
trait Flying { void fly() { println "I'm flying!" }}trait Quacking { void quack() { println 'Quack!' }}class Duck implements Flying, Quacking {}
![Page 20: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/20.jpg)
#s2gx #groovylang @CedricChampeau
Traits : the basics
• Override like any other interface• Compatible with @CompileStatic
20
@CompileStaticclass Duck implements Flying, Quacking { @Override void fly() { println "Duck flying!" quack() }}
![Page 21: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/21.jpg)
#s2gx #groovylang @CedricChampeau
Traits : the basics
• Stateful
21
trait Geolocalized { double longitude double latitude String placeName}class City implements Geolocalized {}
def city = new City(longitude: 32.803468, latitude: -96.769879, placeName: 'Dallas')
![Page 22: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/22.jpg)
#s2gx #groovylang @CedricChampeau
Traits : the basics
• Multiple stateful traits allowed!• Major difference with inheritance
22
trait Inhabited { int population}class City implements Geolocalized, Inhabited {}def city = new City( longitude: 32.803468, latitude: -96.769879, placeName: 'Dallas', population: 1_280_000)println city.population
![Page 23: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/23.jpg)
#s2gx #groovylang @CedricChampeau
Traits : first insight...
• Traits can be designed as small bricks of behavior• Classes can be composed with traits• Increases reusability of components
23
![Page 24: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/24.jpg)
#s2gx #groovylang @CedricChampeau
Traits : beyond basics
• A trait may inherit from another trait
24
trait Named { String name}trait Geolocalized extends Named { long latitude long longitude}class City implements Geolocalized {}
![Page 25: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/25.jpg)
#s2gx #groovylang @CedricChampeau
Traits : beyond basics
• A trait may inherit from multiple traits
25
trait WithId { Long id}trait Place implements Geolocalized, WithId {}
class City implements Place {}
![Page 26: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/26.jpg)
#s2gx #groovylang @CedricChampeau
Traits : beyond basics
• A trait can define abstract methods
26
trait Polite { abstract String getName() void thank() { println "Thank you, my name is $name" }}class Person implements Polite { String name}
def p = new Person(name: 'Rob')p.thank()
![Page 27: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/27.jpg)
#s2gx #groovylang @CedricChampeau
Traits : beyond basics
• Why implements?• From Java
• a trait is only visible as an interface
• default methods of traits are not default methods of interface
• From Groovy
• A trait can be seen as an interface on steroids
• A trait without default implementation is nothing but an interface
27
![Page 28: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/28.jpg)
#s2gx #groovylang @CedricChampeau
Traits : conflicts
• What if two traits define the same method ?
28
trait Worker { void run() { println "I'm a worker!" }}trait Runner { void run() { println "I'm a runner" }}class WhoAmI implements Worker, Runner {}
![Page 29: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/29.jpg)
#s2gx #groovylang @CedricChampeau
Traits : conflicts
• Conflict resolution rule: last trait wins
29
class WhoAmI implements Worker, Runner {}
def o = new WhoAmI()o.run() // prints "runner"
class WhoAmI implements Runner, Worker {}
def o = new WhoAmI()o.run() // prints "worker"
![Page 30: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/30.jpg)
#s2gx #groovylang @CedricChampeau
Traits : explicit conflict resolution
• You can choose which method to call
30
trait Lucky { String adj() { 'lucky'} String name() { 'Alice' }}trait Unlucky { String adj() { 'unlucky'} String name() { 'Bob' }}
class LuckyBob implements Lucky, Unlucky { String adj() { Lucky.super.adj() // we select the "adj" implementation from Lucky } String name() { Unlucky.super.name() // and the "name" implementation from Unlucky } String toString() { "${adj()} ${name()}"}}
def l = new LuckyBob()assert l.toString() == 'lucky Bob'
![Page 31: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/31.jpg)
#s2gx #groovylang @CedricChampeau
Traits : dynamic too!
• Groovy is a dynamic language, so traits should be too!
31
trait ExpandoTrait { private final Map<String,?> values = [:] def propertyMissing(String name) { values.get(name) } void setProperty(String name, value) { println "Setting dynamic property $name" values.put(name, value) }}class Person implements ExpandoTrait { String name}def p = new Person(name: 'Cedric')assert p.name == 'Cedric'p.age = 35assert p.age == 35
![Page 32: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/32.jpg)
#s2gx #groovylang @CedricChampeau
Traits : dynamic too!
• Traits can call methods which are defined in the implementing class
32
class A { void foo() { println 'A' }}
class B {}
trait CallsFooIfAvailable { void bar() { if (metaClass.getMetaMethod('foo')) { foo() } }}class SubA extends A implements CallsFooIfAvailable {}class SubB extends B implements CallsFooIfAvailable {}
[new SubA(), new SubB()]*.bar()
![Page 33: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/33.jpg)
#s2gx #groovylang @CedricChampeau
Traits : what's this?
• “this” represents the trait'ed class
33
trait WhoIsThis { def self() { this }}
class Me implements WhoIsThis {}
def me = new Me()assert me.is(me.self())
![Page 34: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/34.jpg)
#s2gx #groovylang @CedricChampeau
Traits : runtime coercion
• Groovy way of implementing interfaces
34
interface MyListener { void onEvent(String name)}
def explicit = { println it } as MyListenerMyListener implicit = { println it }
explicit.onEvent 'Event 1'implicit.onEvent 'Event 2'
![Page 35: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/35.jpg)
#s2gx #groovylang @CedricChampeau
Traits : runtime coercion
• Same can be done with traits
35
trait Greeter { abstract String getName() void greet() { println "Hello $name" }}
def greeter = { 'Dan' } as GreeterGreeter greeter2 = { 'Dan' }
greeter.greet()greeter2.greet()
![Page 36: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/36.jpg)
#s2gx #groovylang @CedricChampeau
Traits : runtime coercion
• Support for multiple traits at runtime
36
class PoorObject { void doSomething() { println "I can't do much" }}trait SuperPower { void superPower() { println 'But I have superpowers!' }}trait Named { String name}def o = new PoorObject()def named = o as Namednamed.name = 'Bob' // now can set a namenamed.doSomething() // and still call doSomethingdef powered = o as SuperPowerpowered.superPower() // now it has superpowerspowered.doSomething() // it can do somethingdef superHero = o.withTraits(SuperPower, Named) // multiple traits at once!superHero.name = 'SuperBob' // then you can set a namesuperHero.doSomething() // still call doSomethingsuperHero.superPower() // but you have super powers too!
![Page 37: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/37.jpg)
#s2gx #groovylang @CedricChampeau
Traits : runtime coercion gotchas
• Coercion occurs at runtime, at call site• Returned object implements all interfaces and traits
• But doesn't extend the original class!
37
class Person { String name }trait Flying { void fly() { println "$name flying!" }}
def p = new Person(name: 'Thor')def f = p as Flyingassert !p.is(f) // not the same instanceassert f instanceof Flying // it's a Flying objectassert !(f instanceof Person) // but it's not a Person anymore!println f.name // yet, name still existsf.fly()
![Page 38: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/38.jpg)
#s2gx #groovylang @CedricChampeau
Traits : who's fastest?
• Courtesy of Erik Pragt (@epragt)
38
trait NoStackTrace { Throwable fillInStackTrace() { return this }}
class ExpensiveException extends RuntimeException { }class CheapException extends RuntimeException implements NoStackTrace {}
def e1 = new CheapException()def e2 = new ExpensiveException()def e3 = new ExpensiveException() as NoStackTrace
![Page 39: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/39.jpg)
#s2gx #groovylang @CedricChampeau
Traits : who's fastest?
• Courtesy of Erik Pragt (@epragt)
39
user system cpu real
Cheap Exception
42 0 42 43
Expensive Exception
6722 3 6725 6734
Make expensive exception cheap
14982 7 14989 15010
See https://gist.github.com/bodiam/48a06dc9c74a90dfba8c
![Page 40: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/40.jpg)
#s2gx #groovylang @CedricChampeau
Traits : not virtual
• Methods from traits are “copied” where applied
40
trait HasColor { String getColor() { 'red' }}
class Colored implements HasColor {}
def c = new Colored()assert c.color == 'red'
class Colored2 implements HasColor { String getColor() { 'blue' }}
def c2 = new Colored2()assert c2.color == 'blue'
Takes precedence
![Page 41: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/41.jpg)
#s2gx #groovylang @CedricChampeau
Traits : not virtual
• Methods from traits are “copied” where applied
41
class Colored3 { String getColor() { 'yellow' }}class Colored4 extends Colored3 implements HasColor {}
def c4 = new Colored4()assert c4.color == 'red'
Takes precedence
● Traits can be used to share overrides!
![Page 42: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/42.jpg)
#s2gx #groovylang @CedricChampeau
Traits : the meaning of “super”
• super has a special meaning in traits
42
Lucky.super.foo()
Qualified super● But super can also be used for chaining traits
interface MessageHandler { void onMessage(String tag, Map<?,?> payload)}
trait DefaultMessageHandler implements MessageHandler { void onMessage(String tag, Map<?, ?> payload) { println "Received message [$tag]" }}
![Page 43: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/43.jpg)
#s2gx #groovylang @CedricChampeau
Traits : the meaning of “super”
class MyHandler implements DefaultMessageHandler {}
def h = new MyHandler()h.onMessage("event",[:])
![Page 44: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/44.jpg)
#s2gx #groovylang @CedricChampeau
Traits : the meaning of “super”
// A message handler which logs the message and// passes the message to the next handler in the chaintrait ObserverHandler implements MessageHandler { void onMessage(String tag, Map<?, ?> payload) { println "I observed a message: $tag" if (payload) { println "Payload: $payload" } // pass to next handler in the chain super.onMessage(tag, payload) }}
class MyNewHandler implements DefaultMessageHandler, ObserverHandler {}def h2 = new MyNewHandler()h2.onMessage('event 2', [pass:true])
![Page 45: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/45.jpg)
#s2gx #groovylang @CedricChampeau
Traits : chaining with runtime coercion
class BaseHandler implements MessageHandler { @Override void onMessage(final String tag, final Map<?, ?> payload) { println "Received [$tag] from base handler" }}
trait UpperCaseHandler implements MessageHandler { void onMessage(String tag, Map<?, ?> payload) { super.onMessage(tag.toUpperCase(), payload) }}trait DuplicatingHandler implements MessageHandler { void onMessage(String tag, Map<?, ?> payload) { super.onMessage(tag, payload) super.onMessage(tag, payload) }}
def h = new BaseHandler()h = h.withTraits(UpperCaseHandler, DuplicatingHandler, ObserverHandler)h.onMessage('runtime', [:])
![Page 46: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/46.jpg)
Gotchas
46
![Page 47: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/47.jpg)
#s2gx #groovylang @CedricChampeau
Traits : inheritance of state gotchas
• Backing fields are per trait
47
trait IntCouple { int x = 1 int y = 2 int sum() { x+y }}
class BaseElem implements IntCouple { int f() { sum() }}def base = new BaseElem()assert base.f() == 3
![Page 48: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/48.jpg)
#s2gx #groovylang @CedricChampeau
Traits : inheritance of state gotchas
• Backing fields are per trait
48
class Elem implements IntCouple { int x = 3 int y = 4 int f() { sum() }}def elem = new Elem()println elem.f()
What do you think?
![Page 49: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/49.jpg)
#s2gx #groovylang @CedricChampeau
Traits : inheritance of state gotchas
• Backing fields are per trait
49
trait IntCouple { int x = 1 int y = 2 int sum() { getX()+getY() }}
class Elem implements IntCouple { int x = 3 int y = 4 int f() { sum() }}
def elem = new Elem()assert elem.f() == 7
![Page 50: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/50.jpg)
#s2gx #groovylang @CedricChampeau
Traits : inheritance of state gotchas
• Avoids the diamond problem
50
class BaseElem implements IntCouple {}BaseElem.declaredFields.each { println it.name}
// outputsgotchas_IntCouple__xgotchas_IntCouple__y
Fields are “namespaced”
![Page 51: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/51.jpg)
#s2gx #groovylang @CedricChampeau
Traits : AST transformations
51
![Page 52: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/52.jpg)
#s2gx #groovylang @CedricChampeau
Traits : AST transformations
52
• Unless stated differently, consider AST xforms incompatible• Both for technical and logical reasons
• Is the AST xform applied on the trait or should it be applied when the trait is applied?
![Page 53: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/53.jpg)
#s2gx #groovylang @CedricChampeau
Traits : AST transformations
53
• This works
@CompileStatictrait StaticallyCompiledTrait { int x int y int sum() { x + y }}
![Page 54: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/54.jpg)
#s2gx #groovylang @CedricChampeau
Traits : AST transformations
54
• This works too
trait Delegating { @Delegate Map values = [:]}
class Dynamic implements Delegating {}new Dynamic().put('a','b')
![Page 55: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/55.jpg)
#s2gx #groovylang @CedricChampeau
Traits : AST transformations
55
• But this doesn't
trait Named { String name}
@ToString// ToString misses the "name" propertyclass Foo implements Named {}
![Page 56: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/56.jpg)
API design?
56
![Page 57: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/57.jpg)
#s2gx #groovylang @CedricChampeau
Traits : rethinking API design conclusion
57
• Traits let you think about components• Traits can be seen as generalized delegation• Methods can accept traits as arguments• Traits are stackable• Traits are visible as interfaces from Java
![Page 58: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/58.jpg)
#s2gx #groovylang @CedricChampeau
Traits : API independent extension with DSL
58
• DSL can be used to augment an API• No need to subclass the base API
trait ListOfStrings implements List<String> { int totalLength() { sum { it.length() } }}
trait FilteringList implements List<String> { ListOfStrings filter() { findAll { it.contains('G') || it.contains('J') } as ListOfStrings }}
def list = ['Groovy', 'rocks', 'the', 'JVM']println list.withTraits(FilteringList).filter().totalLength()
![Page 59: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/59.jpg)
#s2gx #groovylang @CedricChampeau
Conclusion
59
• Traits extend the benefit of interfaces to concrete classes• Traits do not suffer the diamond problem• Traits allow a new set of API to be written• APIs can be augmented without modifications or extension
http://docs.groovy-lang.org/2.3.6/html/documentation/core-traits.html
![Page 60: Rethinking API Design with Traits](https://reader034.vdocuments.net/reader034/viewer/2022052506/557d5f23d8b42abf3d8b4fd6/html5/thumbnails/60.jpg)
60
@CedricChampeau
melix
http://melix.github.io/blog