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
Hello, Annotation Processing!
Search
Tatsuya Maki
December 10, 2016
Technology
1
240
Hello, Annotation Processing!
Introduction to Annotation Processing.
Tatsuya Maki
December 10, 2016
Tweet
Share
Other Decks in Technology
See All in Technology
Four keys改善の取り組み事例紹介
sansantech
PRO
2
230
巨大なテーブルのテーブル定義を無停止で安全に誰でも変更できるようにする / Table-definitions-for-huge-tables-can-be-modified-by-anyone-safely-and-non-disruptively
freee
1
720
Databricks における 『MLOps』
databricksjapan
2
130
Tebiki株式会社 エンジニア採用資料
tebiki
0
4k
普段有償でサポート業務をしているCSAが技術知見を無料で公開する理由
07jp27
1
630
マルチアカウント環境への発見的統制の導入
ch1aki
1
1.3k
開発生産性向上サービスを作るFindyが自分たちで開発生産性を爆上げした組織づくりの歩み / Findy's path to boosting its own development productivity 2024-04-17
ma3tk
0
240
株式会社EventHub・エンジニア採用資料
eventhub
0
1.9k
クラウドサインにおけるプロダクトマネージャーの役割と開発プロセス / 20240410_cloudsign-PdM
bengo4com
1
670
Signals Unleashed: The Full Guide
rainerhahnekamp
0
350
A (short) History of AI
harishpillay
0
110
ユーザーストーリーのレビューを自動化したみたの
bun913
1
290
Featured
See All Featured
Reflections from 52 weeks, 52 projects
jeffersonlam
343
19k
Building Adaptive Systems
keathley
29
1.8k
Building Applications with DynamoDB
mza
88
5.6k
YesSQL, Process and Tooling at Scale
rocio
162
13k
Designing for Performance
lara
601
67k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
225
51k
GitHub's CSS Performance
jonrohan
1023
450k
Designing with Data
zakiwarfel
95
4.8k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
124
32k
ParisWeb 2013: Learning to Love: Crash Course in Emotional UX Design
dotmariusz
103
6.6k
Practical Orchestrator
shlominoach
181
9.7k
Bash Introduction
62gerente
604
210k
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