Java Gold試験において、地味ながら確実に得点源にしたいのが**「アノテーション」**です。
「@Overrideや@Deprecatedは知っているけど、自作アノテーションの定義方法は?」と聞かれると、意外と手が止まるもの。
この記事では、現役文系エンジニアの視点で、試験で狙われる「メタアノテーション」の法則や自作時の注意点を、図解とクイズ形式で徹底解説します。
1. アノテーションの正体:コードへの「付箋」
アノテーションとは、プログラムそのものの動作に直接影響を与えるのではなく、「このメソッドは上書き用ですよ」「このクラスは古いですよ」といったメタデータ(注釈)を付与する仕組みです。
なぜアノテーションが必要か?
文系出身の私は、アノテーションを**「ソースコードという書類に貼る付箋」**だと解釈しています。
- コンパイラへの指示: 「この付箋があるから、スペルミスがないかチェックしてね」
- ツール・フレームワークへの指示: 「この付箋があるクラスだけ、特別な処理(DIなど)をしてね」
2. 【最重要】自作アノテーションの定義ルール
Java Goldで最も頻出するのが、自作アノテーションの宣言方法です。
Java
public @interface MyAnnotation {
String value(); // 要素(メソッド形式)
int age() default 20; // デフォルト値の設定
}
試験で狙われる3つのポイント
- 宣言キーワード:
interfaceの前に@をつける(@interface)。 - 戻り値の型: プリミティブ、String、Class、列挙型(enum)、アノテーション、およびそれらの配列のみ可能。(ラッパークラスや普通のクラスは不可!)
- 要素の制約: 引数を持つことはできず、
throws句も記述できません。
3. メタアノテーションの「4つの神器」
アノテーションを定義するために、さらにその上に貼るアノテーションを「メタアノテーション」と呼びます。ここが試験の山場です。
| アノテーション | 役割 | 頻出値 |
| @Target | どこに貼れるか? | ElementType.METHOD, TYPE(クラス・インターフェース) |
| @Retention | どこまで生き残るか? | RetentionPolicy.RUNTIME(実行時まで保持) |
| @Documented | Javadocに載せるか? | なし(マーカーアノテーション) |
| @Inherited | 子クラスに継承させるか? | なし |
特に @Retention の SOURCE(コンパイルまで)、CLASS(クラスファイルまで ※デフォルト)、RUNTIME(リフレクションで取得可能)の違いは必ず暗記しましょう。
4. 標準アノテーションと「マーカー」
要素を一つも持たないアノテーションを マーカーアノテーション と呼びます(例:@Override)。
また、Java 8から導入された @Repeatable(同じ場所に複数貼れる)も、最近の試験ではトレンドです。
5. 実戦演習10問:クリックしてチェック!
【例題1】try-with-resourcesの実行順
Java
class MyResource implements AutoCloseable {
String name;
MyResource(String name) { this.name = name; }
public void close() { System.out.print(name + " "); }
}
public class Main {
public static void main(String[] args) {
try (MyResource r1 = new MyResource("A");
MyResource r2 = new MyResource("B")) {
System.out.print("Body ");
}
}
}
A. Body A B B. Body B A C. A B Body D. B A Body
【解説】
正解:B try-with-resourcesでは、リソースは**「宣言された順序の逆順」**でクローズされます。そのため、Body出力後に
r2(B)→r1(A)の順でcloseが実行されます。
【例題2】アサーションの適切な利用 アサーションの使用方法として、適切なものはどれですか?(2つ選択) A. 公開(public)メソッドの引数が有効範囲内かチェックするために使用する。 B. 非公開(private)メソッドの引数が、想定通りの値であるか確認するために使用する。 C. assert (x = 10) > 0; のように、変数の値を更新する処理を含めて記述する。 D. 条件分岐(switch文)の default ラベルなど、「絶対に到達しないはずの場所」に到達していないか確認するために使用する。
【解説】
正解:B, D アサーションは開発者の内部論理を確認するためのものです。publicメソッドの引数チェックは例外を投げるべきであり、また、実行時に無効化される可能性があるため副作用(変数の更新)を伴う処理を書いてはいけません。
【例題3】例外のキャッチ順序 次のコードをコンパイルした際、結果として正しいものはどれですか?
Java
try {
throw new java.io.IOException();
} catch (Exception e) {
System.out.println("Exception");
} catch (java.io.IOException e) {
System.out.println("IOException");
}
A. 「Exception」と表示される B. 「IOException」と表示される C. コンパイルエラーが発生する D. 実行時に例外がスローされる
【解説】
正解:C 例外のキャッチは**「より具体的なサブクラス」**を先に書く必要があります。
Exceptionを先にキャッチしてしまうと、後のIOException句に到達できないためコンパイルエラーになります。
【例題4】例外の伝播とfinally 次のコードを実行したときの結果として、正しいものはどれですか?
Java
public class Main {
public static void main(String[] args) {
try {
method();
} catch (RuntimeException e) {
System.out.print("A ");
} finally {
System.out.print("B ");
}
}
static void method() {
throw new Error();
}
}
A. A B B. B のみ表示され、その後スタックトレースが表示される C. B A D. コンパイルエラーになる
【解説】
正解:B
ErrorはRuntimeExceptionの継承関係にないためキャッチされませんが、finallyは例外の成否に関わらず必ず実行されるため、Bが表示された後に異常終了します。
【例題5】マルチキャッチの制約 次のマルチキャッチの記述のうち、コンパイルエラーになるものはどれですか? A. catch (IOException | SQLException e) B. catch (ArithmeticException | NullPointerException e) C. catch (FileNotFoundException | IOException e) D. catch (RuntimeException | Error e)
【解説】
正解:C マルチキャッチでは、親子関係にある例外を並べて書くことはできません。
IOExceptionはFileNotFoundExceptionの親クラスであるため、コンパイルエラーとなります。
【例題6】アサーションの有効化 アサーションを実行時に有効にするためのコマンドライン引数はどれですか? A. -ea B. -da C. -assertion D. -debug
【解説】
正解:A
enableassertionsを意味する-eaフラグを使用します。デフォルトは無効(-da)です。
【例題7】try-with-resourcesの条件 try-with-resourcesの括弧内で宣言するリソースが、必ず実装していなければならないインターフェースはどれですか? A. java.io.Serializable B. java.lang.AutoCloseable C. java.lang.Runnable D. java.util.stream.Stream
【解説】
正解:B 自動クローズを保証するために、
java.lang.AutoCloseable(またはその子インターフェース)を実装している必要があります。
【例題8】アサーション内での代入 次のコードの説明として正しいものはどれですか? assert x > 0 : x = -1; A. xが0以下の場合、xに-1を代入して処理を続行する B. アサーション失敗時にAssertionErrorが投げられ、詳細メッセージに更新後のxの値が含まれる C. 第2引数で代入操作を行っているため、コンパイルエラーになる D. このアサーションは常に有効であり、無効にできない
【解説】
正解:B アサーションが有効な場合のみ代入式が評価され、その結果が詳細メッセージとして扱われます。ただし、実務上は推奨されない書き方です。
【例題9】抑制された例外(Suppressed Exception) try-with-resourcesにおいて、tryブロックとcloseメソッドの両方で例外が発生した場合、close側の例外はどう扱われますか? A. tryブロックの例外を上書きしてスローされる B. 「抑制された例外」として、メインの例外に付加される C. 無視され、破棄される D. 両方をまとめたMultiExceptionがスローされる
【解説】
正解:B tryブロックでの例外がメインとしてスローされ、リソースのクローズ時に発生した例外は Suppressed Exception(抑制された例外) として内部に保持されます。
【例題10】基本の実行順序 次のコードの出力結果は何ですか?
Java
try {
System.out.print("A ");
throw new RuntimeException();
} catch (Exception e) {
System.out.print("B ");
} finally {
System.out.print("C ");
}
A. A B C B. A C C. A B D. B C
【解説】
正解:A try内の処理実行(A)→例外発生→catchブロック実行(B)→最後に必ずfinally実行(C)の順で出力されます。
まとめ:合格へのチェックリスト
[ ] @interface で宣言し、メソッド形式で要素を作る
[ ] 要素の型にラッパークラスや一般クラスは使えない
[ ] @Retention(RUNTIME) がないと実行時に値は取れない
[ ] value() 要素は、特定の条件下で名前を省略できる
[参考リンク] 現場での活用法を知りたい方は Oracle公式:Javaアノテーション(外部リンク) をチェック。


コメント