$30 off During Our Annual Pro Sale. View Details »
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セキュリティアップデートとAWSを育てる話
cmusudakeisuke
0
270
エンジニアリングマネージャー はじめての目標設定と評価
halkt
0
280
.NET 10の概要
tomokusaba
0
100
寫了幾年 Code,然後呢?軟體工程師必須重新認識的 DevOps
cheng_wei_chen
1
1.4k
Lessons from Migrating to OpenSearch: Shard Design, Log Ingestion, and UI Decisions
sansantech
PRO
1
130
学習データって増やせばいいんですか?
ftakahashi
2
330
生成AI活用の型ハンズオン〜顧客課題起点で設計する7つのステップ
yushin_n
0
160
re:Invent 2025 ~何をする者であり、どこへいくのか~
tetutetu214
0
210
Lambdaの常識はどう変わる?!re:Invent 2025 before after
iwatatomoya
1
490
WordPress は終わったのか ~今のWordPress の制作手法ってなにがあんねん?~ / Is WordPress Over? How We Build with WordPress Today
tbshiki
1
760
AWS Bedrock AgentCoreで作る 1on1支援AIエージェント 〜Memory × Evaluationsによる実践開発〜
yusukeshimizu
6
400
SSO方式とJumpアカウント方式の比較と設計方針
yuobayashi
7
670
Featured
See All Featured
Raft: Consensus for Rubyists
vanstee
141
7.2k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.1k
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
1
100
How STYLIGHT went responsive
nonsquared
100
6k
BBQ
matthewcrist
89
9.9k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.3k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
1.8k
Making the Leap to Tech Lead
cromwellryan
135
9.7k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
7.9k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
Scaling GitHub
holman
464
140k
YesSQL, Process and Tooling at Scale
rocio
174
15k
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