1.概要

この記事では、Java言語のコア機能であるJDKで使用可能なデフォルトのアノテーションについて説明します。

2. 注釈とは

簡単に言うと、アノテーションはJavaタイプであり、その前に「@」記号が付いています。

Javaには、1.5リリース以来ずっとアノテーションがあります。 それ以来、彼らは私たちがアプリケーションを設計する方法を形作っています。

SpringとHibernateは、さまざまな設計手法を可能にするために注釈に大きく依存するフレームワークの優れた例です。

基本的に、アノテーションは、バインドされているソースコードに追加のメタデータを割り当てます。 メソッド、インターフェイス、クラス、またはフィールドにアノテーションを追加することで、次のことができます。

  1. 警告とエラーについてコンパイラに通知します
  2. コンパイル時にソースコードを操作する
  3. 実行時に動作を変更または調査する

3. Java組み込みアノテーション

基本を確認したので、コアJavaに付属しているいくつかのアノテーションを見てみましょう。 まず、コンパイルを通知するものがいくつかあります。

  1. @オーバーライド
  2. @SuppressWarnings
  3. @非推奨
  4. @SafeVarargs
  5. @FunctionalInterface
  6. @ネイティブ

これらの注釈は、コンパイラの警告とエラーを生成または抑制します。 それらを一貫して適用することは、将来のプログラマーのエラーを防ぐことができるため、多くの場合、良い習慣です。

@Override アノテーションは、メソッドが継承されたメソッドの動作をオーバーライドまたは置換することを示すために使用されます。

@SuppressWarnings は、コードの一部からの特定の警告を無視することを示します。 @SafeVarargs アノテーションは、varargsの使用に関連する一種の警告にも作用します。

@Deprecated アノテーションを使用して、APIを使用目的ではないものとしてマークすることができます。 さらに、このアノテーションは Java 9 に後付けされ、非推奨に関する詳細情報を表しています。

これらすべてについて、リンクされている記事でより詳細な情報を見つけることができます。

3.1. @FunctionalInterface

Java 8を使用すると、より機能的な方法でコードを記述できます。

シングル抽象メソッドインターフェイスはこれの大きな部分です。 SAMインターフェースをラムダで使用する場合は、オプションで@FunctionalInterfaceを使用してそのようにマークできます。

@FunctionalInterface
public interface Adder {
    int add(int a, int b);
}

メソッドを使用した@Overrideと同様に、@FunctionalInterfaceAdderを使用して意図を宣言します。

これで、 @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 と同様に、このアノテーションは将来のプログラマーエラーから私たちを保護します。 インターフェイスに複数のメソッドを設定することは合法ですが、そのインターフェイスがラムダターゲットとして使用されている場合はそうではありません。 このアノテーションがないと、コンパイラは数十の場所で壊れてしまいます。 加算器ラムダとして使用されました。 さて、それはAdder自体に侵入するだけです。

3.2.  @Native

Java 8の時点で、に新しいアノテーションがあります。 java.lang.annotation と呼ばれるパッケージネイティブ。  The @ネイティブ注釈はフィールドにのみ適用されます。 注釈付きフィールドがネイティブコードから参照される可能性のある定数であることを示します。 たとえば、整数クラスでの使用方法は次のとおりです。

public final class Integer {
    @Native public static final int MIN_VALUE = 0x80000000;
    // omitted
}

この注釈は、ツールがいくつかの補助ヘッダーファイルを生成するためのヒントとしても役立ちます。

4. メタアノテーション

次に、メタアノテーションは他のアノテーションに適用できるアノテーションです。

たとえば、これらのメタ注釈は注釈の構成に使用されます。

  1. @目標
  2. @保持
  3. @遺伝性の
  4. @Documented
  5. @Repeatable

4.1. @Target

注釈の範囲は、要件に基づいて変わる可能性があります。 1つのアノテーションはメソッドでのみ使用されますが、別のアノテーションはコンストラクターとフィールドの宣言で使用できます。

カスタムアノテーションのターゲット要素を決定するには、@Targetアノテーションでラベルを付ける必要があります。

@Target は、12の異なる要素タイプで動作します。 @SafeVarargs のソースコードを見ると、コンストラクターまたはメソッドにのみアタッチする必要があることがわかります。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface SafeVarargs {
}

4.2. @Retention

一部の注釈はコンパイラーのヒントとして使用することを目的としていますが、他の注釈は実行時に使用されます。

@Retentionアノテーションを使用して、プログラムのライフサイクルのどこにアノテーションが適用されるかを示します

これを行うには、次の3つの保持ポリシーのいずれかを使用して@Retentionを構成する必要があります。

  1. RetentionPolicy.SOURCE –コンパイラにもランタイムにも表示されない
  2. RetentionPolicy.CLASS –コンパイラーによって表示されます
  3. RetentionPolicy.RUNTIME –コンパイラとランタイムに表示

アノテーション宣言に@Retentionアノテーションが存在しない場合、保持ポリシーはデフォルトでRetentionPolicy.CLASSになります。

実行時にアクセスできる必要がある注釈がある場合:

@Retention(RetentionPolicy.RUNTIME)
@Target(TYPE)
public @interface RetentionAnnotation {
}

次に、クラスにいくつかの注釈を追加すると、次のようになります。

@RetentionAnnotation
@Generated("Available only on source code")
public class AnnotatedClass {
}

これで、 AnnotatedClass を振り返って、保持されているアノテーションの数を確認できます。

@Test
public void whenAnnotationRetentionPolicyRuntime_shouldAccess() {
    AnnotatedClass anAnnotatedClass = new AnnotatedClass();
    Annotation[] annotations = anAnnotatedClass.getClass().getAnnotations();
    assertThat(annotations.length, is(1));
}

値は1です。 @RetentionAnnotationにはRUNTIMEの保持ポリシーがありますが、@Generatedにはありません。

4.3. @AliExpress

状況によっては、アノテーションを親クラスにバインドするためにサブクラスが必要になる場合があります。

@AliExpress アノテーションを使用して、アノテーションをアノテーション付きクラスからそのサブクラスに伝播させることができます。

@AliExpress をカスタムアノテーションに適用してから、それを 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));
}

@AliExpress アノテーションがないと、上記のテストは失敗します。

4.4. @Documented

デフォルトでは、JavaはJavaドキュメントでの注釈の使用法を文書化していません。

ただし、@ Documentedアノテーションを使用して、Javaのデフォルトの動作を変更できます。

@Documented を使用するカスタムアノテーションを作成する場合:

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelCell {
    int value();
}

そして、それを適切なJava要素に適用します。

public class Employee {
    @ExcelCell(0)
    public String name;
}

次に、 EmployeeJavadocは注釈の使用法を明らかにします。

4.5. @Repeatable

特定のJava要素に同じアノテーションを複数回指定すると便利な場合があります。

Java 7より前は、アノテーションを1つのコンテナアノテーションにグループ化する必要がありました。

@Schedules({
    @Schedule(time = "15:05"),
    @Schedule(time = "23:00")
})
void scheduledAlarm() {
}

ただし、Java7はよりクリーンなアプローチをもたらしました。 @Repeatableアノテーションを使用すると、アノテーションを繰り返し可能にすることができます

@Repeatable(Schedules.class)
public @interface Schedule {
    String time() default "09:00";
}

使用するには @Repeatable 、コンテナアノテーションも必要です 。 この場合、再利用します @Schedules

public @interface Schedules {
    Schedule[] value();
}

もちろん、これはJava7以前のものとよく似ています。 ただし、現在の値は、 @Schedule を繰り返す必要があるときに、ラッパー@Schedulesが指定されなくなったことです。

@Schedule
@Schedule(time = "15:05")
@Schedule(time = "23:00")
void scheduledAlarm() {
}

Javaにはラッパーアノテーションが必要なため、Java7より前のアノテーションリストから繰り返し可能なアノテーションに簡単に移行できました。

5. 結論

この記事では、すべてのJava開発者が精通している必要があるJava組み込みアノテーションについて説明しました。

いつものように、記事のすべての例はGitHubにあります。