Java組み込みアノテーションの概要
1.
概要
この記事では、Java言語のコア機能、つまりJDKで使用可能なデフォルトの注釈について説明します。
** 2注釈とは
簡単に言うと、アノテーションは「@」記号が前に付いた** Java型です。
Javaは1.5リリース以来ずっと注釈を持っています。それ以来、それらは私達が私達のアプリケーションを設計した方法を形作ってきました。
SpringとHibernateは、さまざまな設計手法を可能にするためにアノテーションに大きく依存しているフレームワークの素晴らしい例です。
基本的に、
アノテーションは
にバインドされているソースコードに追加のメタデータを割り当てます。メソッド、インターフェース、クラス、またはフィールドに注釈を追加することで、次のことが可能になります。
-
警告とエラーについてコンパイラに通知する
-
コンパイル時にソースコードを操作する
-
実行時に動作を変更または確認する
3 Java組み込みアノテーション
基本を見直しましたので、次にコアJavaに付属するアノテーションを見てみましょう。まず、コンパイルを知らせるものがいくつかあります。
-
@オーバーライド
-
@ SuppressWarnings
-
@非推奨
-
@ SafeVarargs
-
@ FunctionalInterface
これらの注釈は、コンパイラの警告とエラーを生成または抑制します。
それらを追加することは将来のプログラマのエラーを防ぐことができるので、それらを一貫して適用することはしばしば良い習慣です。
3.1.
@オーバーライド
サブクラスでは、/java-classes-initialization-questions[オーバーライドまたはオーバーロード]インスタンスメソッドをリンクできます。オーバーライドは、サブクラスが継承された動作を置き換えていることを示します。オーバーロードは、サブクラスが新しい動作を追加しているときです。
実際にオーバーライドしようとしたときに、偶然に過負荷になることがあります。 Javaでこの間違いを犯すのは簡単です:
public class Machine {
public boolean equals(Machine obj) {
return true;
}
@Test
public void whenTwoDifferentMachines__thenReturnTrue() {
Object first = new Machine();
Object second = new Machine();
assertTrue(first.equals(second));
}
}
驚くべきことに、上記のテストは失敗します。これは、この
equals
メソッドがオーバーライドしているのではなく、
Object#equals
をオーバーロードしているためです。
この間違いから私たちを守るために、継承されたメソッドに
@ Override
アノテーションを使うことができます。
この例では、
equals
メソッドの上に
@ Override
アノテーションを追加できます。
@Override
public boolean equals(Machine obj) {
return true;
}
この時点で、コンパイラはエラーを発生させ、私たちが考えているように
equals
をオーバーライドしていないことを知らせます。
それから、私達は私達の間違いを訂正することができます:
@Override
public boolean equals(Object obj) {
return true;
}
誤ってオーバーロードするのは簡単なので、すべての継承メソッドに
@ Override
アノテーションを使用することをお勧めします。
3.2.
@ SuppressWarnings
コンパイラの警告メッセージは通常役に立ちます。時には警告がうるさいことがあります。
特に私たちがそれらに対処することができない、または望まない場合は:
public class Machine {
private List versions;
public void addVersion(String version) {
versions.add(version);
}
}
コンパイラはこのメソッドについて警告を出します。生タイプのコレクションを使用していることを警告します。警告を修正したくない場合は、
@ SuppressWarnings
アノテーションを使って
抑制することができます
。
この注釈により、どの種類の警告を無視するかを指定できます。
警告の種類はhttps://stackoverflow.com/questions/1205995/what-is-the-list-of-valid-suppresswarnings-warning-names-in-java[compiler vendor]によって異なりますが、
2つの最も一般的なものは
deprecation
と
unchecked
。
deprecation
は、非推奨のメソッドや型を使用しているときには無視するようにコンパイラに指示します。
unchecked
は、生の型を使用しているときは無視するようにコンパイラーに指示します。
したがって、上記の例では、** 生の型の使用法に関連する警告を抑制できます。
public class Machine {
private List versions;
@SuppressWarnings("unchecked")
//or
@SuppressWarnings({"unchecked"})
public void addVersion(String version) {
versions.add(version);
}
}
複数の警告のリストを抑制するために、対応する警告リストを含む
String
配列を設定します。
@SuppressWarnings({"unchecked", "deprecated"})
3.3.
@非推奨
プロジェクトが進化するにつれて、そのAPIの変更が行われます。時間が経つにつれて、私たちが人々にもう使いたくないような特定のコンストラクタ、フィールド、タイプ、またはメソッドがあります。
プロジェクトのAPIの下位互換性を破る代わりに、これらの要素に
@ Deprecated
アノテーション
__.
__というタグを付けることができます。
@ Deprecated
は他の開発者に
印の付いた要素はもう
使用されるべきではないことを伝えます
。 Javadocに
@ Deprecated
アノテーションを付けて、正しい振る舞いに役立つ
より良い代替案となるものを説明するのが一般的です。
public class Worker {
/** **
** Calculate period between versions
** @deprecated
** This method is no longer acceptable to compute time between versions.
** <p> Use {@link Utils#calculatePeriod(Machine)} instead.
**
** @param machine instance
** @return computed time
** / @Deprecated
public int calculate(Machine machine) {
return machine.exportVersions().size() ** 10;
}
}
指定されたJava要素がコード内のどこかで使用されている場合、コンパイラは廃止予定のAPI警告のみを表示します。したがって、この場合は、
calculate
メソッドを呼び出すコードがあるかどうかだけを示します。
また、メソッドの説明でJavadoc
@ deprecated
タグを使用して、ドキュメント内で廃止予定のステータスを伝えることもできます。
3.4.
@ SafeVarargs
Java 5では、可変引数の概念、つまり可変長のメソッドパラメータ、およびパラメータ化された型が導入されました。
これらを組み合わせると問題が発生する可能性があります。
public static <T> T[]unsafe(T... elements) {
return elements;//unsafe! don't ever return a parameterized varargs array
}
public static <T> T[]broken(T seed) {
T[]plant = unsafe(seed, seed, seed);//broken! This will be an Object[]no matter what T is
return plant;
}
public static void plant() {
String[]plants = broken("seed");//ClassCastException
}
これらの問題はコンパイラが確認するのが難しいので、__unsafeの場合のように、2つが結合されるたびに警告を出します。
warning:[unchecked]Possible heap pollution from parameterized vararg type T
public static <T> T[]unsafe(T... elements) {
このメソッドは、
broken
のように誤って使用された場合、
は意図した型
b
ではなく
Object[]
配列をヒープに汚染します。
この警告を回避するために、最終メソッドまたは静的メソッドおよびコンストラクタに
@ SafeVarargs
アノテーションを追加することができます。
@ SafeVarargs
は、@ SupressWarnings__と似ていますが、特定のコンパイラ警告が誤検出であることを宣言できるという意味です。
アクションが安全であることを確認したら
、このアノテーションを追加できます。
public class Machine<T> {
private List<T> versions = new ArrayList<>();
@SafeVarargs
public final List<T> safe(T... toAdd) {
for (T version : toAdd) {
versions.add(version);
}
}
}
可変引数を安全に使用することは、それ自体が難しい概念です。詳細については、Josh Blochの著書、https://books.google.com/books?id=BIpDDwAAQBAJで優れた説明があります
3.5.
@ FunctionalInterface
Java 8では、より機能的な方法でコードを書くことができます。
単一抽象メソッドのインターフェース
は、この大部分を占めます。
ラムダで使用されるSAMインタフェースを意図しているなら、
@ FunctionalInterface
でそのようにそれをマークすることができます。
@FunctionalInterface
public interface Adder {
int add(int a, int b);
}
@ Override
がメソッドであるように、
@ FunctionalInterface
は意図を
Adder
で宣言します。
これで、
@ FunctionalInterface
を使用するかどうかにかかわらず、
Adder
を同じ方法で使用できます。
Adder adder = (a,b) -> a + b;
int result = adder.add(4,5);
しかし、__Adderに2番目のメソッドを追加すると、コンパイラは次のように文句を言います。
@FunctionalInterface
public interface Adder {
//compiler complains that the interface is not a SAM
int add(int a, int b);
int div(int a, int b);
}
これで、これは
@ FunctionalInterface
アノテーションなしでコンパイルされました。それで、それは私たちに何を与えるのでしょうか?
@ Override
と同様に、このアノテーションは将来のプログラマのエラーから私たちを守ります。
1つのインタフェースに複数のメソッドを含めることは合法ですが、そのインタフェースがラムダターゲットとして使用されているときではありません。ラムダ。さて、
それは
Adder
自体に割り込むだけです。**
4メタアノテーション
次に、メタアノテーションは他のアノテーションに適用できるアノテーションです。
たとえば、これらのメタアノテーションはアノテーション設定に使用されます。
-
@ターゲット
-
@保持
-
@継承されました
-
@文書化された
-
@繰り返し可能
4.1.
@ターゲット
注釈の範囲は要件によって異なります。注釈はメソッドでのみ使用されますが、別の注釈はコンストラクターとフィールドの宣言で使用できます。
-
カスタムアノテーションのターゲット要素を決定するためには、それに
@ Target
アノテーションを付ける必要があります。**
@ Target
はhttps://docs.oracle.com/javase/tutorial/java/annotations/predefined.html[8種類の要素タイプ]で動作します。 @
SafeVarargs
のソースコードを見ると、それはコンストラクタまたはメソッドにのみ添付されなければならないことがわかります。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {
}
4.2.
@保持
注釈の中には、コンパイラのヒントとして使用されるものもあれば、実行時に使用されるものもあります。
-
私たちは
@ Retention
アノテーションを使って私たちのプログラムのライフサイクルのどこに私たちのアノテーションが適用されるかを示します
これを行うには、次の3つの保存ポリシーのいずれかを使用して
@ Retention
を構成する必要があります。
-
RetentionPolicy.SOURCE –
コンパイラからも見えないし
ランタイム
。
RetentionPolicy.CLASS
– コンパイラから見える
-
RetentionPolicy.RUNTIME –
コンパイラおよびランタイムから見える-
@
Retention
のデフォルトは
RetentionPolicy.SOURCE
です。
-
実行時にアクセス可能なはずの注釈があるとします。
@Retention(RetentionPolicy.RUNTIME)
@Target(TYPE)
public @interface RetentionAnnotation {
}
それでは、クラスにアノテーションを追加すると、
@RetentionAnnotation
@Deprecated
public class AnnotatedClass {
}
保持されている注釈の数を確認するには、
AnnotatedClass
を参照してください。
@Test
public void whenAnnotationRetentionPolicyRuntime__shouldAccess() {
AnnotatedClass anAnnotatedClass = new AnnotatedClass();
Annotation[]annotations = anAnnotatedClass.getClass().getAnnotations();
assertThat(annotations.length, is(1));
}
-
@ @ RetentionAnnotation
の保持ポリシーは
RUNTIME
で、
@ Deprecated__はそうではないため、値は1です。
4.3.
@継承されました
状況によっては、アノテーションを親クラスにバインドするためにサブクラスが必要になることがあります。
注釈を注釈付きクラスからそのサブクラスに伝播させるために
@ Inherited
注釈を使用することができます。
カスタムアノテーションに
@ Inherited
を適用してから
BaseClass
に適用すると、
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface InheritedAnnotation {
}
@InheritedAnnotation
public class BaseClass {
}
public class DerivedClass extends BaseClass {
}
それから、BaseClassを拡張した後、
DerivedClass
は実行時に同じアノテーションを持っているように見えるでしょう:
@Test
public void whenAnnotationInherited__thenShouldExist() {
DerivedClass derivedClass = new DerivedClass();
InheritedAnnotation annotation = derivedClass.getClass()
.getAnnotation(InheritedAnnotation.class);
assertThat(annotation, instanceOf(InheritedAnnotation.class));
}
@ Inherited
アノテーションがないと、上記のテストは失敗します。
4.4.
@文書化された
デフォルトでは、JavaはJavadocでのアノテーションの使い方を文書化していません。
-
しかし、私たちは
@ Documented
アノテーションを使ってJavaのデフォルトの振る舞いを変えることができます** 。
@ Documented
を使用するカスタムアノテーションを作成すると、
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelCell {
int value();
}
そして、それを適切なJava要素に適用します。
public class Employee {
@ExcelCell(0)
public String name;
}
それから
Employee
Javadocはアノテーションの使い方を明らかにします:
画像:/uploads/Screen-Shot-2018-02-08-at-1.38.45-PM-1.png%20868w[画像]
4.5. __ @繰り返し可能
特定のJava要素に同じ注釈を複数回指定すると便利なことがあります。
Java 7より前は、アノテーションを単一のコンテナアノテーションにまとめる必要がありました。
@Schedules({
@Schedule(time = "15:05"),
@Schedule(time = "23:00")
})
void scheduledAlarm() {
}
しかし、Java 7はよりクリーンなアプローチをもたらしました。
@ Repeatable
アノテーションを使用すると、アノテーションを繰り返し可能にすることができます。
@Repeatable(Schedules.class)
public @interface Schedule {
String time() default "09:00";
}
@ Repeatable
を使用するには、コンテナアノテーションも必要です
__.
この場合、
@ Schedules__を再利用します。
public @interface Schedules {
Schedule[]value();
}
もちろん、これはJava 7以前のものと非常によく似ています。しかし、今の価値は、
@ Schedule
を繰り返す必要があるときにラッパー
@ Schedules
が指定されなくなったことです。
@Schedule
@Schedule(time = "15:05")
@Schedule(time = "23:00")
void scheduledAlarm() {
}
Javaにはラッパーアノテーションが必要なので、Java 7より前のアノテーションリストから繰り返し可能なアノテーションに移行するのは簡単でした。
5結論
この記事では、すべてのJava開発者が精通しているべきJava組み込みアノテーションについて説明しました。
いつものように、この記事のすべての例はhttps://github.com/eugenp/tutorials/tree/master/core-java-8[GitHubに掲載]で見つけることができます。