Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Code Generating Your Way to Happiness

Zac Sweers
October 27, 2017

Code Generating Your Way to Happiness

This talk explores how code gen can be used to eliminate a class of issues and sources of developer boilerplate, as well as walking through some popular libraries and uber-specific use cases.

Zac Sweers

October 27, 2017
Tweet

More Decks by Zac Sweers

Other Decks in Programming

Transcript

  1. conferences .flatMap { it.presentations } .onEach { (presentationName, comment, author)

    -> FileSpec.builder("", presentationName)a .addComment(comment) .addAnnotation(AnnotationSpec.builder(Author::class) .addMember("name", "%S", author) .useSiteTarget(FILE) .build()) .build()b } Boilerplate
  2. class Person(val firstName: String, val lastName: String)m class PersonJsonAdapter :

    JsonAdapter<Person>() {a override fun fromJson(reader: JsonReader): Person? {b }k }l
  3. class Person(val firstName: String, val lastName: String)m class PersonJsonAdapter :

    JsonAdapter<Person>() {a override fun fromJson(reader: JsonReader): Person? {b lateinit var firstName: Stringc lateinit var lastName: Stringd }k }l
  4. class Person(val firstName: String, val lastName: String)m class PersonJsonAdapter :

    JsonAdapter<Person>() {a override fun fromJson(reader: JsonReader): Person? {b lateinit var firstName: Stringc lateinit var lastName: Stringd return Person(firstName, lastName)j }k }l
  5. class Person(val firstName: String, val lastName: String)m class PersonJsonAdapter :

    JsonAdapter<Person>() {a override fun fromJson(reader: JsonReader): Person? {b lateinit var firstName: Stringc lateinit var lastName: Stringd while (reader.hasNext()) {e }n return Person(firstName, lastName)j }k }l
  6. class Person(val firstName: String, val lastName: String)m class PersonJsonAdapter :

    JsonAdapter<Person>() {a override fun fromJson(reader: JsonReader): Person? {b lateinit var firstName: Stringc lateinit var lastName: Stringd while (reader.hasNext()) {e when (reader.nextName()) {f }o }n return Person(firstName, lastName)j }k }l
  7. class Person(val firstName: String, val lastName: String)m class PersonJsonAdapter :

    JsonAdapter<Person>() {a override fun fromJson(reader: JsonReader): Person? {b lateinit var firstName: Stringc lateinit var lastName: Stringd while (reader.hasNext()) {e when (reader.nextName()) {f "firstName" -> firstName = reader.nextString()g }o }n return Person(firstName, lastName)j }k }l
  8. class Person(val firstName: String, val lastName: String)m class PersonJsonAdapter :

    JsonAdapter<Person>() {a override fun fromJson(reader: JsonReader): Person? {b lateinit var firstName: Stringc lateinit var lastName: Stringd while (reader.hasNext()) {e when (reader.nextName()) {f "firstName" -> firstName = reader.nextString()g "lastName" -> lastName = reader.nextString()h }o }n return Person(firstName, lastName)j }k }l
  9. class Person(val firstName: String, val lastName: String)m class PersonJsonAdapter :

    JsonAdapter<Person>() {a override fun fromJson(reader: JsonReader): Person? {b lateinit var firstName: Stringc lateinit var lastName: Stringd while (reader.hasNext()) {e when (reader.nextName()) {f "firstName" -> firstName = reader.nextString()g "lastName" -> lastName = reader.nextString()h else -> reader.skipValue()i }o }n return Person(firstName, lastName)j }k }l
  10. class Person(val firstName: String, val lastName: String)m class PersonJsonAdapter :

    JsonAdapter<Person>() {a override fun fromJson(reader: JsonReader): Person? {b lateinit var firstName: Stringc lateinit var lastName: Stringd while (reader.hasNext()) {e when (reader.nextName()) {f "firstname" -> firstName = reader.nextString()g "lastName" -> lastName = reader.nextString()h else -> reader.skipValue()i }o }n return Person(firstName, lastName)j }k }l
  11. class Person(val firstName: String, val lastName: String)m class PersonJsonAdapter :

    JsonAdapter<Person>() {a override fun fromJson(reader: JsonReader): Person? {b lateinit var firstName: Stringc lateinit var lastName: Stringd while (reader.hasNext()) {e when (reader.nextName()) {f "firstname" -> firstName = reader.nextString()g "lastName" -> lastName = reader.nextString()h else -> reader.skipValue()i }o }n return Person(lastName, firstName)j }k }l
  12. class Person(val firstName: String?, val lastName: String)m class PersonJsonAdapter :

    JsonAdapter<Person>() {a override fun fromJson(reader: JsonReader): Person? {b lateinit var firstName: Stringc lateinit var lastName: Stringd while (reader.hasNext()) {e when (reader.nextName()) {f "firstname" -> firstName = reader.nextString()g "lastName" -> lastName = reader.nextString()h else -> reader.skipValue()i }o }n return Person(lastName, firstName)j }k }l
  13. class Person(val firstName: String, val lastName: String)m class City(val name:

    String, val country: String) class Vehicle(val licensePlate: String) class Restaurant(val type: String, val address: Address) class Payment(val cardNumber: String, val type: String) class TipAmount(val value: Double) class Rating(val numStars: Int) class Correctness(val confidence: Double)
  14. Butter Knife TextView title;a ImageView icon;b void onCreate(Bundle savedInstanceState) {c

    title = findViewById(R.id.title);d icon = findViewById(R.id.icon);e }f
  15. Butter Knife @BindView(R.id.title) TextView title;a @BindView(R.id.icon) ImageView icon;b @BindView(R.id.button) Button

    button; void onCreate(Bundle savedInstanceState) {c ButterKnife.bind(this);g }f
  16. ViewBinding( target = "FooActivity", id = 2131361859, name = "title",

    type = "field", viewType = TextView.class )
  17. public final class FooActivity_ViewBinding implements Unbinder { private FooActivity target;

    @UiThread public FooActivity_ViewBinding(FooActivity target, View source) { this.target = target; target.title = Utils.findRequiredViewAsType(source, 2131361859, // R.id.title "field 'title'", TextView.class); }a }b
  18. @BindView(R.id.title) TextView title;a void onCreate(Bundle savedInstanceState) {c ButterKnife.bind(this); }f public

    final class FooActivity_ViewBinding implements Unbinder { private FooActivity target; @UiThread public FooActivity_ViewBinding(FooActivity target, View source) { this.target = target; target.title = Utils.findRequiredViewAsType(source, 2131361859, // R.id.title "field 'title'", TextView.class); }a }b APT Runtime
  19. @AutoValuea abstract class Rider {b abstract String uuid();c abstract String

    firstName();d abstract String lastName();e abstract Address address();f }g Service Gen
  20. struct Rider { 1: required string uuid; 2: required string

    firstName; 3: required string lastName; 4: optional Address address; }
  21. @AutoValuea abstract class Rider {b abstract String uuid();c abstract String

    firstName();d abstract String lastName();e abstract Address address();f }g
  22. @AutoValuea abstract class Rider {b abstract String uuid();c abstract String

    firstName();d abstract String lastName();e abstract Address address();f static JsonAdapter<Rider> jsonAdapter(Moshi moshi) { return new AutoValue_Rider.JsonAdapter(moshi); } }g
  23. astruct Rider struct City struct Vehicle struct Restaurant struct Payment

    struct TipAmount struct Rating // And 6000 more
  24. class ModelsAdapterFactory implements JsonAdapter.Factory { @Override public JsonAdapter<?> create(Type type,

    Set<? extends Annotation> annotations, Moshi moshi) { Class<?> rawType = Types.getRawType(type); if (rawType.isAssignableFrom(Rider.class)) { return Rider.adapter(moshi); } else if (rawType.isAssignableFrom(City.class)) { return City.adapter(moshi); } else if (rawType.isAssignableFrom(Vehicle.class)) { return Vehicle.adapter(moshi); } // Etc etc return null; } }
  25. astruct Rider struct City struct Vehicle struct Restaurant struct Payment

    struct TipAmount struct Rating //aAnd 6000 more
  26. Rider - build.gradle GiftCard - build.gradle Pricing - build.gradle Driver

    - build.gradle EATS - build.gradle Payments - build.gradle
  27. Rider - build.gradle GiftCard - build.gradle Pricing - build.gradle Driver

    - build.gradle EATS - build.gradle Payments - build.gradle
  28. class RiderModelFactory // -> json // -> ridermodelfactory-fractory.bin class MyAppGlobalFactory

    // Delegates to all discovered fractories Fractory C om ing Soon™
  29. struct Rider { 1: required string uuid; 2: required string

    firstName; 3: required string lastName; 4: optional Address address; }a
  30. struct Rider { 1: required string uuid; 2: required string

    firstName; 3: required string lastName; 4: optional Address address; }a
  31. struct Rider { 1: required string uuid; 2: required string

    firstName; 3: required string lastName; }a
  32. struct Rider { 1: required string uuid; 2: required string

    firstName; 3: required string lastName; }a service UberService {a Rider getRider() }b
  33. struct Rider { 1: required string uuid; 2: required string

    firstName; 3: required string lastName; }a service UberService {a Rider getRider() }b
  34. struct Rider { 1: required string uuid; 2: required string

    firstName; 3: required string lastName; }a service UberService {a Rider getRider() }b