Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Hello, Annotation Processing!
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Tatsuya Maki
December 10, 2016
Technology
320
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Hello, Annotation Processing!
Introduction to Annotation Processing.
Tatsuya Maki
December 10, 2016
Other Decks in Technology
See All in Technology
Temporal 最高!でも改めて「日付」について調べたら人類の負債すぎる件
riyaamemiya
0
150
V8のArray.prototype.flatを最大約20倍速くするまでと、巨大OSSへの大規模コミットの道のり
riyaamemiya
0
300
スキマバイトという雇用形態は存在しないので、雇用期間1日のアルバイト契約を事業者とワーカーが結ぶ手続きを支えるのがタイミーなんです
akitoshiga
0
200
20261017福岡devIO 「AIは"良いダッシュボード"を作れるか?
jtakeda
0
170
ストレスなくAIと付き合いたい!こんなときどうしてる?
namiki64
3
560
オントロジーって 結局なにもの? AI エージェントに「意味」を渡す知識設計の話
greenmidori
0
220
Railsで挑む大量データ永続化 〜600億件を永続化するための設計とボトルネックとの戦い〜
occhi
1
600
LambdaWebFunctionsを試してみた / exploring-lambda-web-functions
koukihosaka
1
350
DGX Spark でどこまでハッキングができるのか?
kash1064
3
1.3k
WasmをRustにする
sansantech
PRO
0
250
Browser Spec: Testing Hotwire with Ruby
takahashim
1
120
Classmethod Company Deck
satoshi7
1
700
Featured
See All Featured
Building AI with AI
inesmontani
PRO
1
1.3k
brightonSEO & MeasureFest 2025 - Winning Strategies for Black Friday CRO & PPC - Christian Goodrich
cargoodrich
3
870
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
430
From π to Pie charts
rasagy
1
400
SEO for Brand Visibility & Recognition
aleyda
0
4.8k
Lightning Talk: Beautiful Slides for Beginners
inesmontani
PRO
2
730
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.4k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3.1k
Why Our Code Smells
bkeepers
PRO
340
58k
Context Engineering - Making Every Token Count
addyosmani
9
1.2k
Reality Check: Gamification 10 Years Later
codingconduct
0
2.3k
Transcript
@Hello("Annotation Processing!") December 10, 2016 Tatsuya Maki
2. Annotation Processorをつくる 1. Annotation Processingをしる 3. Annotation Processorをつかう
2. Annotation Processorをつくる 1. Annotation Processingをしる 3. Annotation Processorをつかう
AutoValue https://github.com/google/auto
@AutoValue public abstract class User { public abstract String name();
public abstract int age(); }
final class AutoValue_User extends User { private final String name;
private final int age; AutoValue_User(String name, int age) { if (name == null) { throw new NullPointerException("Null name"); } this.name = name; this.age = age; } @Override public String name() { return name; } @Override public int age() { return age; } @Override public String toString() { return "User{" + "name=" + name + ", " + "age=" + age + "}"; } @Override public boolean equals(Object o) { if (o == this) { return true; }
Butter Knife https://github.com/JakeWharton/butterknife/
public class MainActivity extends AppCompatActivity { @BindView(R.id.fab) FloatingActionButton fab;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } @OnClick(R.id.fab) void showSnackBar(View view) { Snackbar.make(view, "FloatingActionButton is clicked!", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); } }
public class MainActivity_ViewBinding<T extends MainActivity> implements Unbinder { protected T
target; private View view2131492974; @UiThread public MainActivity_ViewBinding(final T target, View source) { this.target = target; View view; view = Utils.findRequiredView(source, R.id.fab, "field 'fab' and method 'showSnackBar'"); target.fab = Utils.castView(view, R.id.fab, "field 'fab'", FloatingActionButton.class); view2131492974 = view; view.setOnClickListener(new DebouncingOnClickListener() { @Override public void doClick(View p0) { target.showSnackBar(p0); } }); } @Override @CallSuper public void unbind() { T target = this.target; if (target == null) throw new IllegalStateException("Bindings already cleared."); target.fab = null; view2131492974.setOnClickListener(null); view2131492974 = null; this.target = null; } }
Dagger 2 https://github.com/google/dagger
@ActivityScope @Component(modules = {ActivityModule.class}) public interface ActivityComponent { void inject(@NonNull
MainActivity activity); }
public final class DaggerActivityComponent implements ActivityComponent { private DaggerActivityComponent(Builder builder)
{ assert builder != null; } public static Builder builder() { return new Builder(); } public static ActivityComponent create() { return builder().build(); } @Override public void inject(MainActivity activity) { MembersInjectors.<MainActivity>noOp().injectMembers(activity); } public static final class Builder { private Builder() { } public ActivityComponent build() { return new DaggerActivityComponent(this); } public Builder activityModule(ActivityModule activityModule) { Preconditions.checkNotNull(activityModule); return this; } } }
Annotation Processingは、 JSR 269で定義されている仕様。 コンパイル時にアノテーションを処理する。 Pluggable Annotation Processor APIのこと。
*.java *.class
*.java Parse & Enter 構文解析し、シンボルテーブルへ入力 *.class
*.java Parse & Enter Annotation Processing アノテーションを処理 *.class
*.java Parse & Enter Annotation Processing 新しいファイルが生成されると再処理 *.class
*.java *.class Parse & Enter Annotation Processing Analyze & Generate
構文木を分析し、classファイルに変換
ソースコードを生成するので安心。 実行時に余計な処理をしないので高速。 コンパイル時に型が検証できるので安全。 Reflectionと比較して、
2. Annotation Processorをつくる 1. Annotation Processingをしる 3. Annotation Processorをつかう
Annotationの実装 2 1 3 AnnotationProcessorの実装 AnnotationProcessorを登録
Annotationの実装 2 1 3 AnnotationProcessorの実装 AnnotationProcessorを登録
@Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface MyAnnotation { String message(); }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface MyAnnotation { String message(); }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface MyAnnotation { String message(); }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface MyAnnotation { String message(); }
@Target(ElementType.TYPE) @Retention(RetentionPolicy.CLASS) public @interface MyAnnotation { String message(); }
Annotationの実装 2 1 3 AnnotationProcessorの実装 AnnotationProcessorを登録
public interface Processor { Set<String> getSupportedOptions(); Set<String> getSupportedAnnotationTypes();
SourceVersion getSupportedSourceVersion(); void init(ProcessingEnvironment processingEnv); boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv); Iterable<? extends Completion> getCompletions(Element element, AnnotationMirror annotation, ExecutableElement member, String userText); }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes()
{ return Collections.singleton(MyAnnotation.class.getCanonicalName()); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public synchronized void init(ProcessingEnvironment environment) { super.init(environment); messager = environment.getMessager(); elements = environment.getElementUtils(); types = environment.getTypeUtils(); filer = environment.getFiler(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment environment) { ... } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes()
{ return Collections.singleton(MyAnnotation.class.getCanonicalName()); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public synchronized void init(ProcessingEnvironment environment) { super.init(environment); messager = environment.getMessager(); elements = environment.getElementUtils(); types = environment.getTypeUtils(); filer = environment.getFiler(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment environment) { ... } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes()
{ return Collections.singleton(MyAnnotation.class.getCanonicalName()); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public synchronized void init(ProcessingEnvironment environment) { super.init(environment); messager = environment.getMessager(); elements = environment.getElementUtils(); types = environment.getTypeUtils(); filer = environment.getFiler(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment environment) { ... } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes()
{ return Collections.singleton(MyAnnotation.class.getCanonicalName()); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public synchronized void init(ProcessingEnvironment environment) { super.init(environment); messager = environment.getMessager(); elements = environment.getElementUtils(); types = environment.getTypeUtils(); filer = environment.getFiler(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment environment) { ... } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes()
{ return Collections.singleton(MyAnnotation.class.getCanonicalName()); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public synchronized void init(ProcessingEnvironment environment) { super.init(environment); messager = environment.getMessager(); elements = environment.getElementUtils(); types = environment.getTypeUtils(); filer = environment.getFiler(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment environment) { ... } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public Set<String> getSupportedAnnotationTypes()
{ return Collections.singleton(MyAnnotation.class.getCanonicalName()); } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } @Override public synchronized void init(ProcessingEnvironment environment) { super.init(environment); messager = environment.getMessager(); elements = environment.getElementUtils(); types = environment.getTypeUtils(); filer = environment.getFiler(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment environment) { ... } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<?
extends TypeElement> annotations, RoundEnvironment environment) { final Set<? extends Element> elements = environment.getElementsAnnotatedWith(MyAnnotation.class); elements.forEach(element -> { final Name className = element.getSimpleName(); final Name packageName = this.elements.getPackageOf(element).getQualifiedName(); try { final JavaFileObject file = filer.createSourceFile(packageName + ".MyAnnotation_" + className); try (final Writer writer = file.openWriter()) { writer.append("package ").append(packageName).append(";\n\n") .append("public class MyAnnotation_").append(className).append(" {\n\n") .append(" @Override\n") .append(" public String toString() {\n"); final MyAnnotation annotation = element.getAnnotation(MyAnnotation.class); final String message = annotation.message(); writer.append(" return \"").append(message).append("\";\n") .append(" }\n") .append("}\n"); writer.flush(); } } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate a file:" + packageName); } }); return false; } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<?
extends TypeElement> annotations, RoundEnvironment environment) { final Set<? extends Element> elements = environment.getElementsAnnotatedWith(MyAnnotation.class); elements.forEach(element -> { final Name className = element.getSimpleName(); final Name packageName = this.elements.getPackageOf(element).getQualifiedName(); try { final JavaFileObject file = filer.createSourceFile(packageName + ".MyAnnotation_" + className); try (final Writer writer = file.openWriter()) { writer.append("package ").append(packageName).append(";\n\n") .append("public class MyAnnotation_").append(className).append(" {\n\n") .append(" @Override\n") .append(" public String toString() {\n"); final MyAnnotation annotation = element.getAnnotation(MyAnnotation.class); final String value = annotation.value(); writer.append(" return \"").append(value).append("\";\n") .append(" }\n") .append("}\n"); writer.flush(); } } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate a file:" + packageName); } }); return false; } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<?
extends TypeElement> annotations, RoundEnvironment environment) { final Set<? extends Element> elements = environment.getElementsAnnotatedWith(MyAnnotation.class); elements.forEach(element -> { final Name className = element.getSimpleName(); final Name packageName = this.elements.getPackageOf(element).getQualifiedName(); try { final JavaFileObject file = filer.createSourceFile(packageName + ".MyAnnotation_" + className); try (final Writer writer = file.openWriter()) { writer.append("package ").append(packageName).append(";\n\n") .append("public class MyAnnotation_").append(className).append(" {\n\n") .append(" @Override\n") .append(" public String toString() {\n"); final MyAnnotation annotation = element.getAnnotation(MyAnnotation.class); final String value = annotation.value(); writer.append(" return \"").append(value).append("\";\n") .append(" }\n") .append("}\n"); writer.flush(); } } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate a file:" + packageName); } }); return false; } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<?
extends TypeElement> annotations, RoundEnvironment environment) { final Set<? extends Element> elements = environment.getElementsAnnotatedWith(MyAnnotation.class); elements.forEach(element -> { final Name className = element.getSimpleName(); final Name packageName = this.elements.getPackageOf(element).getQualifiedName(); try { final JavaFileObject file = filer.createSourceFile(packageName + ".MyAnnotation_" + className); try (final Writer writer = file.openWriter()) { writer.append("package ").append(packageName).append(";\n\n") .append("public class MyAnnotation_").append(className).append(" {\n\n") .append(" @Override\n") .append(" public String toString() {\n"); final MyAnnotation annotation = element.getAnnotation(MyAnnotation.class); final String value = annotation.value(); writer.append(" return \"").append(value).append("\";\n") .append(" }\n") .append("}\n"); writer.flush(); } } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate a file:" + packageName); } }); return false; } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<?
extends TypeElement> annotations, RoundEnvironment environment) { final Set<? extends Element> elements = environment.getElementsAnnotatedWith(MyAnnotation.class); elements.forEach(element -> { final Name className = element.getSimpleName(); final Name packageName = this.elements.getPackageOf(element).getQualifiedName(); try { final JavaFileObject file = filer.createSourceFile(packageName + ".MyAnnotation_" + className); try (final Writer writer = file.openWriter()) { writer.append("package ").append(packageName).append(";\n\n") .append("public class MyAnnotation_").append(className).append(" {\n\n") .append(" @Override\n") .append(" public String toString() {\n"); final MyAnnotation annotation = element.getAnnotation(MyAnnotation.class); final String value = annotation.value(); writer.append(" return \"").append(value).append("\";\n") .append(" }\n") .append("}\n"); writer.flush(); } } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate a file:" + packageName); } }); return false; } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<?
extends TypeElement> annotations, RoundEnvironment environment) { final Set<? extends Element> elements = environment.getElementsAnnotatedWith(MyAnnotation.class); elements.forEach(element -> { final Name className = element.getSimpleName(); final Name packageName = this.elements.getPackageOf(element).getQualifiedName(); try { final JavaFileObject file = filer.createSourceFile(packageName + ".MyAnnotation_" + className); try (final Writer writer = file.openWriter()) { writer.append("package ").append(packageName).append(";\n\n") .append("public class MyAnnotation_").append(className).append(" {\n\n") .append(" @Override\n") .append(" public String toString() {\n"); final MyAnnotation annotation = element.getAnnotation(MyAnnotation.class); final String value = annotation.value(); writer.append(" return \"").append(value).append("\";\n") .append(" }\n") .append("}\n"); writer.flush(); } } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate a file:" + packageName); } }); return false; } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<?
extends TypeElement> annotations, RoundEnvironment environment) { final Set<? extends Element> elements = environment.getElementsAnnotatedWith(MyAnnotation.class); elements.forEach(element -> { final Name className = element.getSimpleName(); final Name packageName = this.elements.getPackageOf(element).getQualifiedName(); try { final JavaFileObject file = filer.createSourceFile(packageName + ".MyAnnotation_" + className); try (final Writer writer = file.openWriter()) { writer.append("package ").append(packageName).append(";\n\n") .append("public class MyAnnotation_").append(className).append(" {\n\n") .append(" @Override\n") .append(" public String toString() {\n"); final MyAnnotation annotation = element.getAnnotation(MyAnnotation.class); final String message = annotation.message(); writer.append(" return \"").append(message).append("\";\n") .append(" }\n") .append("}\n"); writer.flush(); } } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate a file:" + packageName); } }); return false; } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<?
extends TypeElement> annotations, RoundEnvironment environment) { final Set<? extends Element> elements = environment.getElementsAnnotatedWith(MyAnnotation.class); elements.forEach(element -> { final Name className = element.getSimpleName(); final Name packageName = this.elements.getPackageOf(element).getQualifiedName(); try { final JavaFileObject file = filer.createSourceFile(packageName + ".MyAnnotation_" + className); try (final Writer writer = file.openWriter()) { writer.append("package ").append(packageName).append(";\n\n") .append("public class MyAnnotation_").append(className).append(" {\n\n") .append(" @Override\n") .append(" public String toString() {\n"); final MyAnnotation annotation = element.getAnnotation(MyAnnotation.class); final String message = annotation.message(); writer.append(" return \"").append(message).append("\";\n") .append(" }\n") .append("}\n"); writer.flush(); } } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate a file:" + packageName); } }); return false; } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<?
extends TypeElement> annotations, RoundEnvironment environment) { final Set<? extends Element> elements = environment.getElementsAnnotatedWith(MyAnnotation.class); elements.forEach(element -> { final Name className = element.getSimpleName(); final Name packageName = this.elements.getPackageOf(element).getQualifiedName(); try { final JavaFileObject file = filer.createSourceFile(packageName + ".MyAnnotation_" + className); try (final Writer writer = file.openWriter()) { writer.append("package ").append(packageName).append(";\n\n") .append("public class MyAnnotation_").append(className).append(" {\n\n") .append(" @Override\n") .append(" public String toString() {\n"); final MyAnnotation annotation = element.getAnnotation(MyAnnotation.class); final String message = annotation.message(); writer.append(" return \"").append(message).append("\";\n") .append(" }\n") .append("}\n"); writer.flush(); } } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate a file:" + packageName); } }); return false; } }
public class MyAnnotationProcessor extends AbstractProcessor { @Override public boolean process(Set<?
extends TypeElement> annotations, RoundEnvironment environment) { final Set<? extends Element> elements = environment.getElementsAnnotatedWith(MyAnnotation.class); elements.forEach(element -> { final Name className = element.getSimpleName(); final Name packageName = this.elements.getPackageOf(element).getQualifiedName(); try { final JavaFileObject file = filer.createSourceFile(packageName + ".MyAnnotation_" + className); try (final Writer writer = file.openWriter()) { writer.append("package ").append(packageName).append(";\n\n") .append("public class MyAnnotation_").append(className).append(" {\n\n") .append(" @Override\n") .append(" public String toString() {\n"); final MyAnnotation annotation = element.getAnnotation(MyAnnotation.class); final String value = annotation.value(); writer.append(" return \"").append(value).append("\";\n") .append(" }\n") .append("}\n"); writer.flush(); } } catch (IOException e) { messager.printMessage(Diagnostic.Kind.ERROR, "Failed to generate a file:" + packageName); } }); return false; } }
Annotationの実装 2 1 3 AnnotationProcessorの実装 AnnotationProcessorを登録
. |--src | |-- main | | |-- java |
| | `-- io.t28.example.MyAnnotationProcessor.java | | `-- resources | | `-- META-INF.services | | `-- javax.annotation.processing.Processor | `-- test | `-- java | `-- io.t28.example.MyAnnotationProcessorTest.java `-- build.gradle
. |--src | |-- main | | |-- java |
| | `-- io.t28.example.MyAnnotationProcessor.java | | `-- resources | | `-- META-INF.services | | `-- javax.annotation.processing.Processor | `-- test | `-- java | `-- io.t28.example.MyAnnotationProcessorTest.java `-- build.gradle
2. Annotation Processorをつくる 1. Annotation Processingをしる 3. Annotation Processorをつかう
dependencies { compile project(':library') annotationProcessor project(':processor') }
@MyAnnotation(message = "Hello, world.") public class MyClass { }
public class MyAnnotation_MyClass { @Override public String toString() {
return "Hello, world."; } }
まとめ
2. コンパイル時にコードの検証や生成が可能 1. Pluggable Annotation Processing 3. ボイラープレートコードの生成に効果的
おまけ
AutoService https://github.com/google/auto/tree/master/service Compile Testing https://github.com/google/compile-testing JavaPoet https://github.com/square/javapoet
@Thanks