Javaアノテーションインタビューの質問(+回答)
1. 前書き
アノテーションはJava 5以来存在しており、今日では、コードを充実させることができるユビキタスなプログラミング構成要素です。
この記事では、アノテーションに関するいくつかの質問を確認します。技術面接でよく尋ねられ、必要に応じて;回答をよりよく理解するために例を実装します。
2. 質問
Q1. 注釈とは それらの典型的なユースケースは何ですか?
注釈は、プログラムのソースコードの要素にバインドされたメタデータであり、操作するコードの動作には影響しません。
それらの典型的な使用例は次のとおりです。
-
Information for the compiler –注釈を使用すると、コンパイラーはエラーを検出したり、警告を抑制したりできます
-
Compile-time and deployment-time processing –ソフトウェアツールは注釈を処理し、コード、構成ファイルなどを生成できます。
-
Runtime processing –注釈を実行時に調べて、プログラムの動作をカスタマイズできます
Q2. 標準ライブラリからのいくつかの有用な注釈を説明してください。
java.langおよびjava.lang.annotationパッケージにはいくつかの注釈があり、より一般的な注釈には次のものが含まれますが、これらに限定されません。
-
@Override –は、メソッドがスーパークラスで宣言された要素をオーバーライドすることを意図していることを示します。 メソッドを正しくオーバーライドできない場合、コンパイラはエラーを発行します
-
@Deprecated –要素が非推奨であり、使用しないことを示します。 プログラムがこの注釈でマークされたメソッド、クラス、またはフィールドを使用する場合、コンパイラは警告を発行します
-
@SuppressWarnings –特定の警告を抑制するようにコンパイラーに指示します。 ジェネリックが登場する前に記述されたレガシーコードとインターフェースするときに最も一般的に使用されます
-
@FunctionalInterface – Java 8で導入された、型宣言が関数型インターフェースであり、その実装がLambda式を使用して提供できることを示します
Q3. どうすれば注釈を作成できますか?
アノテーションは、キーワードinterfaceの前に@,があり、その本体にメソッドと非常によく似たannotation type element宣言が含まれているインターフェイスの形式です。
public @interface SimpleAnnotation {
String value();
int[] types();
}
注釈が定義されると、yonはコードを介して注釈の使用を開始できます。
@SimpleAnnotation(value = "an element", types = 1)
public class Element {
@SimpleAnnotation(value = "an attribute", types = { 1, 2 })
public Element nextElement;
}
配列要素に複数の値を指定する場合は、それらを角かっこで囲む必要があることに注意してください。
オプションで、コンパイラーへの定数式である限り、デフォルト値を提供できます。
public @interface SimpleAnnotation {
String value() default "This is an element";
int[] types() default { 1, 2, 3 };
}
これで、これらの要素なしで注釈を使用できます。
@SimpleAnnotation
public class Element {
// ...
}
またはそれらの一部のみ:
@SimpleAnnotation(value = "an attribute")
public Element nextElement;
Q4. アノテーションメソッド宣言から返すことができるオブジェクトタイプは何ですか?
戻り値の型は、プリミティブ、String、Class、Enum、または前の型のいずれかの配列である必要があります。 そうでない場合、コンパイラはエラーをスローします。
この原則に正常に従うサンプルコードは次のとおりです。
enum Complexity {
LOW, HIGH
}
public @interface ComplexAnnotation {
Class extends Object> value();
int[] types();
Complexity complexity();
}
Objectは有効な戻り値の型ではないため、次の例はコンパイルに失敗します。
public @interface FailingAnnotation {
Object complexity();
}
Q5. どのプログラム要素に注釈を付けることができますか?
注釈は、ソースコード全体のいくつかの場所に適用できます。 クラス、コンストラクター、およびフィールドの宣言に適用できます。
@SimpleAnnotation
public class Apply {
@SimpleAnnotation
private String aField;
@SimpleAnnotation
public Apply() {
// ...
}
}
メソッドとそのパラメーター:
@SimpleAnnotation
public void aMethod(@SimpleAnnotation String param) {
// ...
}
ループおよびリソース変数を含むローカル変数:
@SimpleAnnotation
int i = 10;
for (@SimpleAnnotation int j = 0; j < i; j++) {
// ...
}
try (@SimpleAnnotation FileWriter writer = getWriter()) {
// ...
} catch (Exception ex) {
// ...
}
その他の注釈タイプ:
@SimpleAnnotation
public @interface ComplexAnnotation {
// ...
}
そして、パッケージでさえ、package-info.javaファイルを介して:
@PackageAnnotation
package com.example.interview.annotations;
Java 8以降、これらは型のuseにも適用できます。 これが機能するためには、注釈でElementType.USEの値を持つ@Target注釈を指定する必要があります。
@Target(ElementType.TYPE_USE)
public @interface SimpleAnnotation {
// ...
}
これで、クラスインスタンスの作成に注釈を適用できます。
new @SimpleAnnotation Apply();
型キャスト:
aString = (@SimpleAnnotation String) something;
実装句:
public class SimpleList
implements @SimpleAnnotation List<@SimpleAnnotation T> {
// ...
}
そしてthrows節:
void aMethod() throws @SimpleAnnotation Exception {
// ...
}
Q6. 注釈を適用できる要素を制限する方法はありますか?
はい、@Targetアノテーションをこの目的に使用できます。 適用できないコンテキストで注釈を使用しようとすると、コンパイラはエラーを発行します。
@SimpleAnnotationアノテーションの使用をフィールド宣言のみに制限する例を次に示します。
@Target(ElementType.FIELD)
public @interface SimpleAnnotation {
// ...
}
より多くのコンテキストで適用できるようにする場合は、複数の定数を渡すことができます。
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PACKAGE })
注釈を作成して、何かに注釈を付けることができないようにすることもできます。 これは、宣言された型が複雑な注釈のメンバー型としてのみ使用することを意図している場合に便利です。
@Target({})
public @interface NoTargetAnnotation {
// ...
}
Q7. メタアノテーションとは何ですか?
他の注釈に適用される注釈です。
@Target,でマークされていない、またはマークされているがANNOTATION_TYPE定数を含むすべての注釈も、メタ注釈です。
@Target(ElementType.ANNOTATION_TYPE)
public @interface SimpleAnnotation {
// ...
}
Q8. 繰り返し注釈とは何ですか?
これらは、同じ要素宣言に複数回適用できる注釈です。
互換性の理由から、この機能はJava 8で導入されたため、繰り返し注釈はJavaコンパイラによって自動的に生成されるcontainer annotationに格納されます。 コンパイラーがこれを行うには、それらを宣言する2つのステップがあります。
まず、繰り返し可能な注釈を宣言する必要があります。
@Repeatable(Schedules.class)
public @interface Schedule {
String time() default "morning";
}
次に、必須のvalue要素を使用して包含アノテーションを定義します。そのタイプは、繰り返し可能なアノテーションタイプの配列である必要があります。
public @interface Schedules {
Schedule[] value();
}
これで、@ Scheduleを複数回使用できます。
@Schedule
@Schedule(time = "afternoon")
@Schedule(time = "night")
void scheduledMethod() {
// ...
}
Q9. 注釈を取得するにはどうすればよいですか? これは保持ポリシーとどのように関連していますか?
Reflection APIまたは注釈プロセッサを使用して、注釈を取得できます。
@RetentionアノテーションとそのRetentionPolicyパラメータは、それらを取得する方法に影響を与えます。 RetentionPolicy列挙型には3つの定数があります。
-
RetentionPolicy.SOURCE –注釈をコンパイラーによって破棄しますが、注釈プロセッサーはそれらを読み取ることができます
-
RetentionPolicy.CLASS –注釈がクラスファイルに追加されているが、リフレクションではアクセスできないことを示します
-
RetentionPolicy.RUNTIME –注釈はコンパイラーによってクラスファイルに記録され、実行時にJVMによって保持されるため、反射的に読み取ることができます。
実行時に読み取ることができるアノテーションを作成するためのサンプルコードは次のとおりです。
@Retention(RetentionPolicy.RUNTIME)
public @interface Description {
String value();
}
これで、リフレクションを介して注釈を取得できます。
Description description
= AnnotatedClass.class.getAnnotation(Description.class);
System.out.println(description.value());
注釈プロセッサはRetentionPolicy.SOURCEで動作できます。これについては、Java Annotation Processing and Creating a Builderの記事で説明されています。
RetentionPolicy.CLASSは、Javaバイトコードパーサーを作成するときに使用できます。
Q10. 次のコードはコンパイルされますか?
@Target({ ElementType.FIELD, ElementType.TYPE, ElementType.FIELD })
public @interface TestAnnotation {
int[] value() default {};
}
No. 同じ列挙型定数が@Targetアノテーションに複数回出現する場合は、コンパイル時エラーです。
重複する定数を削除すると、コードが正常にコンパイルされます。
@Target({ ElementType.FIELD, ElementType.TYPE})
Q11. アノテーションを拡張することは可能ですか?
No. 注釈は、Java Language Specificationで説明されているように、常にjava.lang.annotation.Annotation,を拡張します。
アノテーション宣言でextends句を使用しようとすると、コンパイルエラーが発生します。
public @interface AnAnnotation extends OtherAnnotation {
// Compilation error
}
結論
この記事では、注釈に関して、Java開発者向けの技術面接で表示されるよくある質問のいくつかを取り上げました。 これは決して完全なリストではなく、さらなる研究の開始と見なされるだけです。
たとえば、今後のインタビューで成功することを願っています。