NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

注目のタグ

    アノテーションつくってみた

    1. はじめに

    皆さんこんにちは!2024年度新入社員の松澤です。
    配属先ではJavaを使ってごりごり開発に携わる予定です!!

    皆さんはJava100本ノックをやったことはありますか?Javaに精通した諸先輩方には物足りないかもしれないですが、研修で初級程度のJavaしか触ったことのないペーペーの新入社員には結構難解です。わからないところだらけですが、なんとか調べながら解いているところです。

    その中で、おもろいやんと思ったアノテーションの自作について今回はレポートしていきます!


    2. アノテーションってなんやねん

    JavaJavaしまくった方ならもうご存じかもしれないですが、そうです。@Overrideとかのあれです。初級Java研修の時には@Overrideぐらいしか出なかったのに次に受講したSpring研修では@￰PathVariable@￰OneToMany@￰GetMappingとかわんさかでてきて覚えるのに苦労しました…。

    アノテーションは日本語で「注釈」という意味があります。一般的な書籍を読んでいると、引用元などが注釈として記載されている場合があり、ちょこっと便利ですよね~!

    Javaでもアノテーションを使うことでクラス・メソッド・フィールド等に付属情報を付けることができます。

    2.1. アノテーションの特徴

    そんなアノテーションですが以下のような特徴があります。

    • アノテーションは@ + 名前のような形で表現される。
    • クラスやメソッドに対して補足的な情報をつけることができる。

    2.2. アノテーションのメリット

    他にも以下のようなメリットもあります。

    • コンパイラと組み合わせて適切な処理が行われないとエラーを起こしたり逆にエラーを抑制したりできる。
      • @Override:オーバーライドしていなかったら警告を表示。
      • @SuppressWarnings:コンパイラによる警告を抑制するためのアノテーション。引数でどのような警告を抑制するかを指定できる。
    • アノテーションをもとにして、コンパイル時に新しいクラスを生成できる。
    • リフレクションAPIを用いて、特定のアノテーションを対象にして処理を行うことができる。(具体例は後述します)

    いろいろ便利な機能があるんですね~。


    3. アノテーションつくってみた

    アノテーションの特徴を述べまくりましたが、アノテーションは自作することができます。 今回は、AuthorAnnotaionというアノテーションを作成して、AuthorAnnotaionからコード編集者を判定するプログラムを作成していきたいと思います。

    ※通常はJavadocで@authorタグにコード編集者を記載しますが、今回はアノテーションを作成するのが目的なので、AuthorAnnotaionを用いて編集者を判定します。

    3.1. @interfaceによりアノテーションを定義

    アノテーションを定義する際には@interfaceキーワードを指定します。
    さらにフィールドを指定することで、アノテーションで利用できる属性を指定することができます。 今回のプログラムでは、アノテーションに指定した属性を用いて編集者を分類するので、author属性を定義します。defaultキーワードでデフォルト値を指定します。

    メタアノテーションの定義

    メタアノテーションとは、アノテーションに関する「適用範囲」や「保持期限」などを制御するアノテーションです。

    • @Target:クラス内のどこを対象として付与するアノテーションであるかを定義
      • 今回はElementType.FIELDを指定してフィールドのみに付与させる。
    • @Retention:アノテーションの保持期間を定義
      • 今回はRetentionPolicy.RUNTIMEを指定し、このプログラムの実行中にアノテーション情報を取得できるようにします。

    AuthorAnnotation.java

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    //アノテーション情報をJVMに保持し、実行中にアノテーション情報を取得できるようにする
    @Retention(RetentionPolicy.RUNTIME)
    //フィールドに付与するアノテーションであることを示す
    @Target({ElementType.FIELD})
    public @interface AuthorAnnotation {
        // AuthorAnnotaionに付与する編集者情報
        String author() default "noname";
    }
    

    3.2. アノテーションを付与する

    先ほど作成した@AuthorAnnotationAttachクラスに付与していきます。
    AuthorAnnotationインターフェースで@Target({ElementType.FIELD})を指定したので、フィールドにアノテーションを付与していきます。

    今回は編集者に関するフィールドを4つ作成しました。

    • me friend :それぞれauthor属性に値を指定
    • shiranhitoauthor属性に値を指定しない(デフォルト値が適用)
    • senpai@AuthorAnnotationを付与しない

    Attach.java

    public class Attach {
        //フィールドにアノテーションを付与
        @AuthorAnnotation(author = "Matsuzawa")
        final private String me = "Matsuzawa";
    
        @AuthorAnnotation(author = "friend")
        final private String friend = "friend";
        
        @AuthorAnnotation()
        final private String shiranhito = "shiranhito";
        
        private String senpai;
    }
    

    3.3. アノテーションを活用する

    MainクラスにreturnJudgeResultメソッドを定義することによって編集者判定を行いました。

    1. Attachクラスのインスタンスを作成
    2. リフレクションを用いてフィールドにアタッチされるauthorを抽出
    3. 抽出したauthor値からif文で編集者判定

    リフレクションによって、Classの型やFieldの値を動的に呼び出すことができます。今回はFieldの情報を取得したいのでFieldクラスを用いて情報を取得しました。

    さらに、アノテーションが付与されない場合はauthorがNullになってしまうのでNullPointerExceptionをcatchしました。

    Main.java

    import java.lang.reflect.Field;
    
    public class Main {
        public static void main(String[] args) throws NoSuchFieldException {
            //編集者判定
            printJudgeResult("me");
            printJudgeResult("friend");
            printJudgeResult("shiranhito");
            printJudgeResult("senpai");
        }
    
        /**
         * AttachクラスのフィールドにアタッチされたAuthorAnnotaionのauthor値から
         * 編集者判定結果を返却する
         *
         * @param fieldName
         * @throws NoSuchFieldException
         */
        private static void printJudgeResult(String fieldName) throws NoSuchFieldException {
            try {
                // Attachクラスのインスタンス作成
                final Attach attach = new Attach();
    
                // リフレクションを用いてフィールドにアタッチされるauthorを抽出
                final Field field = attach.getClass().getDeclaredField(fieldName);
                final AuthorAnnotation anotation = field.getAnnotation(AuthorAnnotation.class);
                final String author = anotation.author();
    
                //authorによる判定
                if (author.equals("Matsuzawa")) {
                    System.out.println(fieldName + ":編集者は松澤武志です");
                } else if (author.equals("noname")) {
                    System.out.println(fieldName + ":編集者の登録がありません");
                } else {
                    System.out.println(fieldName + ":編集者は" + author + "です");
                }
    
                // アノテーションが付与されていない場合に例外処理を発生させる
            } catch (NullPointerException e) {
                System.out.println(fieldName + ":アノテーションが付与されてへんで");
            }
        }
    }
    

    3.4. 出力結果

    こちらのメインメソッドを実行すると以下の出力結果が得られました。
    出力結果

    me:編集者は松澤武志です
    friend:編集者はfriendです
    shiranhito:編集者の登録がありません
    senpai:アノテーションが付与されてへんで

    author属性に指定された文字によって分類されていることがわかります!


    4. 最後に

    こんな感じでアノテーションを作ってみました。意外と簡単に作ることができましたが @SuppressWarningsのように警告を抑制するようなアノテーションもいつか自作出来たら良いですね~。 アノテーションを作成する機会自体は、開発を行う上でも結構出て来るらしいので、また作りまくっていきたいと思います!
    最後までご覧いただきありがとうございました!皆さんもアノテーションを作りまくってみてください~。

    執筆者 松澤武志

    2025 Japan AWS Jr. Champion
    趣味でAWSをいじるネイティブアプリエンジニア(.NET/C#/Java/Angular)

    ・執筆記事一覧: https://tech.nri-net.com/archive/author/t-matsuzawa
    ・X:https://x.com/2357_takeshi
    ・Speaker Deck:https://speakerdeck.com/matsuzawatakeshi
    ・Qiita:https://qiita.com/takeshi18