Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
How can we write less code by generating code?
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Gil Goldzweig
April 24, 2018
Programming
1
52
How can we write less code by generating code?
Gil Goldzweig
April 24, 2018
Tweet
Share
More Decks by Gil Goldzweig
See All by Gil Goldzweig
Reactive UI in Android (Jetpack compose)
gilgoldzweig
0
380
Kotlin-Workshop Day #3
gilgoldzweig
0
18
Kotlin Workshop day #2
gilgoldzweig
0
170
Kotlin-Workshop Day #1
gilgoldzweig
0
24
Google IO recap
gilgoldzweig
0
23
Other Decks in Programming
See All in Programming
Rust 製のコードエディタ “Zed” を使ってみた
nearme_tech
PRO
0
110
AI Agent の開発と運用を支える Durable Execution #AgentsInProd
izumin5210
7
2.2k
今から始めるClaude Code超入門
448jp
7
8k
humanlayerのブログから学ぶ、良いCLAUDE.mdの書き方
tsukamoto1783
0
170
CSC307 Lecture 04
javiergs
PRO
0
650
Implementation Patterns
denyspoltorak
0
270
Basic Architectures
denyspoltorak
0
650
AI時代のキャリアプラン「技術の引力」からの脱出と「問い」へのいざない / tech-gravity
minodriven
14
4.6k
カスタマーサクセス業務を変革したヘルススコアの実現と学び
_hummer0724
0
490
SourceGeneratorのススメ
htkym
0
180
プロダクトオーナーから見たSOC2 _SOC2ゆるミートアップ#2
kekekenta
0
180
AI前提で考えるiOSアプリのモダナイズ設計
yuukiw00w
0
220
Featured
See All Featured
GraphQLとの向き合い方2022年版
quramy
50
14k
First, design no harm
axbom
PRO
2
1.1k
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.7k
エンジニアに許された特別な時間の終わり
watany
106
230k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
Designing for Performance
lara
610
70k
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
1
110
Test your architecture with Archunit
thirion
1
2.1k
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
5.2k
Music & Morning Musume
bryan
47
7.1k
Unsuck your backbone
ammeep
671
58k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Transcript
How can we write less code by generating code? (Not
skynet) 24/4/2018
Gil Goldzweig https://github.com/gilgoldzweig Android developer @ NYX
None
How can we write less code by generating code? 10/01/2019
Annotation Processing
Annotation Processor tool - APT • A tool for the
Javac(Java compiler) • Added JDK 1.5 • Reads & Process @Annotated sources
Chain of events Write (.java) Compile (.class) Package (.jar) Dex
(.dex) Package (.apk) APT (.java) Project lifecycle - APT
Project lifecycle Write (.java) Compile (.class) Package (.jar)
Chain of events Write (.java) Compile (.class) Package (.jar) Dex
(.dex) Package (.apk) Project lifecycle - android
Example
Before - Gencycler RecyclerView
Before - Gencycler ViewHolder class LectureViewHolder extends RecyclerView.ViewHolder { TextView
title; TextView description; TextView date; LectureViewHolder(View itemView) { super(itemView); title = itemView.findViewById(R.id.lecture_title); description = itemView.findViewById(R.id.lecture_desc); date = itemView.findViewById(R.id.lecture_date); } }
Before - Gencycler Adapter - OnCreateViewHolder public class LectureAdapter extends
RecyclerView.Adapter<LectureViewHolder> { … @Override public LectureViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new LectureViewHolder(inflater .inflate(R.layout.item_lecture, parent, false)); } }
Before - Gencycler Adapter - GetItemCount public class LectureAdapter extends
RecyclerView.Adapter<LectureViewHolder> { … @Override public int getItemCount() { return lectures.size(); } }
Before - Gencycler Adapter - GetItemViewType public class LectureAdapter extends
RecyclerView.Adapter<LectureViewHolder> { … @Override public int getItemViewType(int position) { return super.getItemViewType(position); } }
Before - Gencycler RecyclerView - OnBindViewHolder public class LectureAdapter extends
RecyclerView.Adapter<LectureViewHolder> { … @Override public void onBindViewHolder(LectureViewHolder holder, int position) { Lecture lecture = lectures.get(position); } }
Fun Fact 81
Gencycler
RecyclerView + Gencycler public class LectureAdapter { } Recyclerview -
Gencycler
RecyclerView + Gencycler @RecyclerAdapter(data = Lecture.class, ui = R.layout.item_lecture) public
class LectureAdapter { } Recyclerview - Gencycler
RecyclerView + Gencycler @RecyclerAdapter public class LectureAdapter { } Recyclerview
- Gencycler
RecyclerView + Gencycler @RecyclerAdapter(data = Lecture.class) public class LectureAdapter {
} Recyclerview - Gencycler
RecyclerView + Gencycler @RecyclerAdapter(data = Lecture.class, ui = R.layout.item_lecture) public
class LectureAdapter { } Recyclerview - Gencycler
RecyclerView + Gencycler @RecyclerAdapter(data = Lecture.class, ui = R.layout.item_lecture) public
class LectureAdapter { } Recyclerview - Gencycler
RecyclerView + Gencycler @RecyclerAdapter(data = Lecture.class, ui = R.layout.item_lecture) public
class LectureAdapter extends GeneratedLectureAdapter { } Recyclerview - Gencycler
RecyclerView + Gencycler @RecyclerAdapter(data = Lecture.class, ui = R.layout.item_lecture) public
class LectureAdapter extends GeneratedLectureAdapter { @Override public void onBindLectureViewHolder(LectureViewHolder holder, int position, @NotNull Lecture lecture) { //Actual logic (setText etc… ) } } Recyclerview - Gencycler
7 Lines
81 vs 7 Lines
Questions?
1. Annotations 2. Processor 2.1 Code Generator Components
1. Annotations 2. Processor 2.1 Code Generator Components
Annotation public interface RecyclerAdapter {}
Annotation public @interface RecyclerAdapter {}
Annotation @Target(ElementType.TYPE) public @interface RecyclerAdapter {}
Target - Limit where the annotation can be placed •
Type(Class) • Field • Method • Parameter • AnnotationType • Constructor • Package • LocalVariable (Java 1.8) • TypeParameter (Java 1.8)
Annotation @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface RecyclerAdapter {}
Retention - When will the annotation can be used •
Source(Annotation processor) • Runtime(Reflection) • Class(Annotation processor + Reflection)
Reflection Reflection • Create new instances of objects • Invoke
methods • Examine properties of classes (fields, methods, constructors) • Modify field values
Reflection Reflection
Reflection Reflection - drawbacks • No compile-time verification • Breaks
easily • Expensive • Hard to maintain
Annotation @Retention(RetentionPolicy.SOURCE) //Compile-time @Target(ElementType.TYPE) //Can be placed on a class
public @interface RecyclerAdapter {}
Questions?
1. Annotations 2. Processor 2.1 Code Generator Components
AbstractProcessor public class RecyclerProcessor extends AbstractProcessor { } Processor
AbstractProcessor public class RecyclerProcessor extends AbstractProcessor { } Processor
AbstractProcessor public class RecyclerProcessor extends AbstractProcessor { @Override public synchronized
void init(ProcessingEnvironment env) { } } Processor
AbstractProcessor public class RecyclerProcessor extends AbstractProcessor { @Override public synchronized
void init(ProcessingEnvironment env) { } } Processor
AbstractProcessor ProcessingEnvironment • Filer - used to create new source,
class, or auxiliary files • Opptions • Messenger - Reports errors, warnings, and other notices • Utility methods for using elements/types
AbstractProcessor public class RecyclerProcessor extends AbstractProcessor { … @Override public
Set<String> getSupportedAnnotationTypes() { return ImmutableSet.of(RecyclerAdapter.class .getCanonicalName()); } } Processor
AbstractProcessor public class RecyclerProcessor extends AbstractProcessor { … @Override public
SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } } Processor
AbstractProcessor public class RecyclerProcessor extends AbstractProcessor { … @Override public
boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { } } Processor
AbstractProcessor public class RecyclerProcessor extends AbstractProcessor { … @Override public
boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { } } Processor
AbstractProcessor public class RecyclerProcessor extends AbstractProcessor { … @Override public
boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { } } Processor
None
AbstractProcessor Java sources APT Completed Round one - sources LectureAdapter.java
OtherAdapter.java Processing rounds
AbstractProcessor Java sources APT Completed Round one - sources Processing
rounds LectureAdapter.java OtherAdapter.java Processing rounds
AbstractProcessor public class RecyclerProcessor extends AbstractProcessor { … @Override public
boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { //RoundEnvironment will hold information about //the LectureAdapter.java & OtherAdapter.Java } } Processor
AbstractProcessor Java sources APT Completed Round one - new source
files generated Processing rounds LectureGenrated.java OtherGenrated.java LectureAdapter.java OtherAdapter.java Processing rounds
AbstractProcessor Processing rounds Java sources APT Completed Round one -
Completed Processing rounds LectureGenrated.java OtherGenrated.java LectureAdapter.java OtherAdapter.java Processing rounds
AbstractProcessor Processing rounds Java sources APT Completed Round two -
generated sources Processing rounds LectureGenrated.java OtherGenrated.java LectureAdapter.java OtherAdapter.java Processing rounds Processing rounds Processing rounds
AbstractProcessor Processing rounds Java sources APT Completed Processing rounds LectureAdapter.java
OtherAdapter.java LectureGenrated.java OtherGenrated.java Round two - generated sources Processing rounds
AbstractProcessor Processing rounds Java sources APT Completed LectureGenrated.java OtherGenrated.java Round
two - Completed Processing rounds LectureAdapter.java OtherAdapter.java Processing rounds
Questions?
1. Annotations 2. Processor 2.1 Code Generator Components
• Automatic ◦ Java/KotlinPoet (open source library created by square)
• Manual ◦ Writer Code generation Code generation
Pros & Cons
+ Easy usage + Very good documentation + Language objects
oriented Automatic - Java/kotlinPoet - Very verbose code
+ Easy usage Manual - StringBuilder - Not language objects
oriented - Hard to maintain properly Manual - Writer
Example
package com.example.helloworld; public final class HelloWorld { public static void
main(String[] args) { System.out.println("Hello, JavaPoet!"); } } What we want to generate
JavaPoet - method MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class)
.addParameter(String[].class, "args") .addStatement("System.out.println(\"Hello, JavaPoet!\")") .build();
JavaPoet - method MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class)
.addParameter(String[].class, "args") .addStatement("System.out.println(\"Hello, JavaPoet!\")") .build();
JavaPoet - method MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class)
.addParameter(String[].class, "args") .addStatement("System.out.println(\"Hello, JavaPoet!\")") .build();
JavaPoet - method MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class)
.addParameter(String[].class, "args") .addStatement("System.out.println(\"Hello, JavaPoet!\")") .build();
JavaPoet - method MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class)
.addParameter(String[].class, "args") .addStatement("System.out.println(\"Hello, JavaPoet!\")") .build();
JavaPoet - method MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class)
.addParameter(String[].class, "args") .addStatement("System.out.println(\"Hello, JavaPoet!\")") .build();
MethodSpec public static void main(String[] args) { System.out.println("Hello, JavaPoet!"); }
JavaPoet - method / generated
JavaPoet - class TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main)
.build(); JavaPoet - class
JavaPoet - class TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main)
.build(); JavaPoet - method JavaPoet - class
JavaPoet - class TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main)
.build(); JavaPoet - method JavaPoet - class
JavaPoet - class TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main)
.build(); JavaPoet - class
MethodSpec public final class HelloWorld { public static void main(String[]
args) { System.out.println("Hello, JavaPoet!"); } } JavaPoet - class JavaPoet - class JavaPoet - class / generated
JavaPoet - file JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build(); JavaPoet
- file
package com.example.helloworld; public final class HelloWorld { public static void
main(String[] args) { System.out.println("Hello, JavaPoet!"); } } JavaPoet - file / generated
package com.example.helloworld; public final class HelloWorld { public static void
main(String[] args) { System.out.println("Hello, JavaPoet!"); } } JavaPoet - file / generated
JavaPoet - file JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build(); javaFile.writeTo(processingEnv.getFiler());
JavaPoet - file
JavaPoet MethodSpec main = MethodSpec.methodBuilder("main") .addModifiers(Modifier.PUBLIC, Modifier.STATIC) .returns(void.class) .addParameter(String[].class, "args")
.addStatement("System.out.println(\"Hello, JavaPoet!\")") .build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(main) .build(); JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld) .build(); javaFile.writeTo(processingEnv.getFiler()); JavaPoet JavaPoet - code needed
JavaPoet - file Manual Writer codeWriter = procesingEnc.filer .createSource(StandardLocation.SOURCE_OUTPUT) .openWriter();
codeWriter .append("package com.example.helloworld;") .append("\n\n") .append("public final class HelloWorld {\n") .append(" ") .append("public static void main(String[] args) {\n") .append(" ") .append("System.out.println(\"Hello, JavaPoet!\");\n") .append(" }\n") .append("}");
AbstractProcessor • Resources ◦ META_INF ▪ services • javax.annotations.processing.Processor package.name.compiler.RecyclerProcessor
Another processor if exist And another ... Declaring our processor
Benefits • No Runtime performance impact • Write your code
generator once • Trust the generated code Benefits
Thank you
Here are some helpful links to help you build your
own processor :) Annotation Processing : Don’t Repeat Yourself, Generate Your Code. Annotation processing 101 @Eliminate(Boilerplate) JavaPoet KotlinPoet Gencycler