倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

いまさら!? Class クラス (4) : クラス宣言

Java の Class クラスに定義されているメソッドを見ていくシリーズ(目次)。 今回は Class クラスに定義されているメソッドのうち、クラスの宣言に関するメソッドを見ていきます。

クラス宣言に関するメソッド

クラス宣言に関するメソッド*1は、ここでは以下のような Java コードに含まれている要素とします:

package org.sample;

@MyAnnotation
public class MyClass<N extends Number>{
    // ...
}

つまり、以下の要素とします:

クラス階層(継承や実装など)に関連するメソッドは今回は扱いません(おそらく次回)。 今回扱うメソッドを列挙すると

メソッド名
パッケージ getPackage()
アノテーション isAnnotationPresent(Class<? extends Annotation> aType)
getAnnotations()
<A extends Annotation> getAnnotation(Class<A> aType)
getDeclaredAnnotations()
修飾子 getModifiers()
型パラメータ getTypeParameters()

です。

パッケージ Package

パッケージ関連のメソッドは下表の通り:

メソッド名 返り値
getPackage() Package

Package クラスの完全修飾名は java.lang.Package です。 使い方は上記の MyClass クラスに対して

Class<?> type = MyClass.class
Package pack = type.getPackage();
System.out.println(pack.getName());    // "org.sample" と表示

といった感じです。 Package クラスの詳細は JavaDoc 参照。

アノテーション Annotation

アノテーション関連のメソッドは以下の通り:

メソッド名 返り値 スコープ
getAnnotations()
isAnnotationPresent(Class<? extends Annotation> aType)
<A extends Annotation> getAnnotation(Class<A> aType)
Annotation[]
boolean
A
親クラス、
実装インターフェースを含む
getDeclaredAnnotations() Annotation[] 対象とする型のみ

Annotation インターフェースの完全修飾名は java.lang.annotation.Annotation です。 また、これらのメソッドは java.lang.reflect.AnnotatedElement に定義されています。

アノテーションを取得できる条件
上記のメソッドを使ってプログラム中からアノテーションを取得できるためには、アノテーションのリテンション・ポリシーが RUNTIME でなければなりません:

@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation{}

また、親クラスや実装インターフェースに付加してあるアノテーションが取得できるためには、さらに @Inherited が付加されている必要があります:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface SuperAnnotation{}

アノテーションの取得方法
ここではアノテーションの取得方法を見ていきます。 扱うアノテーションはすべて先ほどの「アノテーションを取得できる条件」を満たしているとします。

getDeclaredAnnotations() メソッドとその他のメソッドでは、対象とするアノテーションが少し違います。getDeclaredAnnotations() メソッドは親クラス・実装インターフェースにさかのぼらずに、対象としている型に付加されているアノテーションのみを返します。 一方、getDeclaredAnnotations() メソッド以外は、親クラス・実装インターフェースさかのぼってアノテーションを検索します。

例えば以下のように SuperClass クラスと MyClass クラスが定義されているとします:

@SuperAnnotation
public class SuperClass{...}

@MyAnnotation
public class MyClass extends SuperClass{...}

@MyAnnotation, @SuperAnnotation アノテーションは先ほどのように定義されているとします。 このとき

Class<?> type = MyClass.class;

type.getAnnotations();    // 配列 [@MyAnnotation, @SuperAnnoation] が返される
assert type.isAnnotationPresent(@MyAnnotation.class) == true;
assert type.isAnnoationPresent(@SuperAnnotation.class) = true;
assert type.getAnnotation(@MyAnnotation.class) != null;
assert type.getAnnotation(@SuperAnnotation.class) != null;

type.getDeclaredAnnotations();    // 配列 [@MyAnnotation] が返される

となります。

アノテーション関連メソッドのまとめ
結果をまとめると

対象クラスのアノテーション 親クラス・実装インターフェース
アノテーション
getAnnotations()
isAnnotationPresent()
getAnnotation()

(RetentionPolicy:RUNTIME)

(RetentionPolicy:RUNTIME, Inherited)
getDeclaredAnnotations()
(RetentionPolicy:RUNTIME)
×

○:取得可能 ×:取得不可能
となります。

修飾子 Modifier

修飾子関連のメソッドは下表の通り:

メソッド名 返り値
getModifiers() int

修飾子を判定するには java.lang.reflect.Modifier クラスに定義されている static メソッドを用います:

Class<?> type = MyClass.class;
int mod = type.getModifiers();
System.out.println(Modifier.isPublic(mod));    // "true" と表示

型パラメータ Type Parameter

型パラメータに関連するメソッドは下表の通り(Class<T> に対するメソッド):

メソッド名 返り値
getTypeParameters() TypeVariable<Class<T>>[]

このメソッドは java.lang.reflect.GenericDeclaration に定義されているメソッドです。 使い方は、記事最初の MyClass クラスに対して

Class<?> type = MyClass.class;
TypeVariable<?> tv = type.getTypeParameters()[0];

System.out.println(tv.getGenericDeclaration());    // "class org.sample.MyClass" と表示 (MyClass.class.toString())
System.out.println(tv.getName());                    // "N" と表示
System.out.println(tv.getBounds()[0]);            // "class java.lang.Number" と表示 (Number.class.toString())

といった感じです。 TypeVariable インターフェース(完全修飾名 java.lang.reflect.TypeVariable)の詳細については JavaDoc 参照。

追記
id:irof 氏にご指摘いただきましたが、アノテーションを取得するメソッドは @Inherited アノテーションが付いているアノテーションを、“親クラス”だけをさかのぼって探し、“実装インターフェース”は参照しないようです。

Effective Java 第2版 (The Java Series)

Effective Java 第2版 (The Java Series)

*1:一般に使われている言葉ではありませんが。