Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Hello, Annotation Processing!
Search
Tatsuya Maki
December 10, 2016
Technology
1
290
Hello, Annotation Processing!
Introduction to Annotation Processing.
Tatsuya Maki
December 10, 2016
Tweet
Share
Other Decks in Technology
See All in Technology
AWSに革命を起こすかもしれない新サービス・アップデートについてのお話
yama3133
0
510
Snowflake導入から1年、LayerXのデータ活用の現在 / One Year into Snowflake: How LayerX Uses Data Today
civitaspo
0
2.5k
"人"が頑張るAI駆動開発
yokomachi
1
620
100以上の新規コネクタ提供を可能にしたアーキテクチャ
ooyukioo
0
260
[Data & AI Summit '25 Fall] AIでデータ活用を進化させる!Google Cloudで作るデータ活用の未来
kirimaru
0
4k
普段使ってるClaude Skillsの紹介(by Notebooklm)
zerebom
8
2.3k
_第4回__AIxIoTビジネス共創ラボ紹介資料_20251203.pdf
iotcomjpadmin
0
140
AR Guitar: Expanding Guitar Performance from a Live House to Urban Space
ekito_station
0
240
「もしもデータ基盤開発で『強くてニューゲーム』ができたなら今の僕はどんなデータ基盤を作っただろう」
aeonpeople
0
250
AIエージェント開発と活用を加速するワークフロー自動生成への挑戦
shibuiwilliam
5
870
AWS運用を効率化する!AWS Organizationsを軸にした一元管理の実践/nikkei-tech-talk-202512
nikkei_engineer_recruiting
0
170
ソフトウェアエンジニアとAIエンジニアの役割分担についてのある事例
kworkdev
PRO
0
290
Featured
See All Featured
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
7.9k
The SEO identity crisis: Don't let AI make you average
varn
0
39
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.2k
Claude Code のすすめ
schroneko
67
210k
Data-driven link building: lessons from a $708K investment (BrightonSEO talk)
szymonslowik
1
860
Optimising Largest Contentful Paint
csswizardry
37
3.5k
Mobile First: as difficult as doing things right
swwweet
225
10k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.3k
Paper Plane (Part 1)
katiecoart
PRO
0
2.1k
Embracing the Ebb and Flow
colly
88
4.9k
The Director’s Chair: Orchestrating AI for Truly Effective Learning
tmiket
0
67
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.7k
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