倭マン's BLOG

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

いまさら!? Class クラス (6) : メンバへのアクセス

Java の Class クラスに定義されているメソッドを見ていくシリーズ(目次)。 今回はメンバへのアクセスを行うメソッド。 ここで、メンバとして扱うのは

  • フィールド
  • コンストラクタ
  • メソッド
  • 内部クラス

の4つです。

メンバへのアクセス一般

各メンバを見ていく前に、まず一般的注。

まず、各 Member (Field, Constructor, Method, Class) に対して、宣言されているもの配列として返すメソッドには getMembers(), getDeclaredMembers() (「Member」の部分には Field, Constructor, Method, Classe のいずれかが入ります)の2つがあります。 これらの違いは下表のようになっています:

メソッド 返り値 返すメンバのスコープ 親クラス・実装インターフェース
getMembers() Member[] public さかのぼる
getDeclaredMembers() Member[] public, protected,
package(default), private
さかのぼらない

クラス図風に表すとこんな感じ:

f:id:waman:20171005234031p:plain

また、Field, Constructor, Method には、引数を渡して、それによって一意に指定されるメンバを返すメソッドも用意されています:

メンバ 引数 引数の説明
Field String フィールドの名前
Construtor Class<?>... コンストラクタの引数の型
Method String, Class<?>... メソッドの名前, メソッドの引数の型

以下で、各メンバをもう少し詳しく見ていきます。

フィールド Field

まずはフィールド。 フィールドを一意に指定するのはフィールド名を表す文字列です。

メソッド名 返り値
getFields()
getField(String name)
Field[]
Field
getDeclaredFields()
getDeclaredField(String name)
Field[]
Field

サンプル・コードを見てみましょう。 SuperClass クラスとそれを継承する MyClass クラス、それらのいろいろなスコープのフィールドを以下のように定義します:

public class SuperClass{
    private   String fSuperPrivate;
              String fSuperPackage;
    protected String fSuperProtected;
    public    String fSuperPublic;
}

public class MyClass extends SuperClass{
    private   String fPrivate;
              String fPackage;
    protected String fProtected;
    public    String fPublic;
}

このとき

    Field[] fields0 = MyClass.class.getFields();
        // [fPrivate, fPackage, fProtected, fPublic] の配列が返される

    Field[] fields1 = MyClass.class.getDeclaredFields();
        // [fPublic, fSuperPublic] の配列が返される

    Field field = MyClass.class.getField("fPublic");
        // フィールド MyClass.fPublic が返される

となります。

コンストラクタ Constructor

次はコンストラクタ。 コンストラクタを一意に指定するのは、引数の型の列です。

メソッド名 返り値
getConstructors()
getConstructor(Class<?>... parameterTypes)
Constructor<?>[]
Constructor<T>
getDeclaredConstructors()
getDeclaredConstructor(Class<?>... parameterTypes)
Constructor<?>[]
Constructor<T>

複数のコンストラクタを返すメソッドはフィールドの場合と同じなので省略。 コンストラクタをひとつ返すメソッドのサンプル:

    Constructor c = Exception.class
                             .getConstructor(String.class, Throwable.class);
        // コンストラクタ Exception(String message, Throwable ex) が返される

メソッド Method

メソッドを返すメソッド。 名前がある以外はコンストラクタと変わりなし(返り値もあるけど)。

メソッド名 返り値
getMethods()
getMethod(String name, Class<?>... parameterTypes)
Method[]
Method
getDeclaredMethods()
getDeclaredMethod(String name, Class<?>... parameterTypes)
Method[]
Method

メソッドを複数返すメソッドは、オーバーロードされている*1メソッドは別メソッドとして返されます*2。 これに関連して、メソッドを1つ返すメソッドは、メソッド名に加えて引数の型も渡す必要があります。

    Method m = String.class.getMethod("substring", int.class);
        // メソッド String.substring(int) が返される

    Method m_ = String.class.getMethod("substring", Integer.class);
        // 例外 NoSuchMethodException が投げられる

1つのメソッドを取得する際に引数として渡す型は、プリミティブ型が要求されているならプリミティブ型のクラス (int.class など) を渡す必要があります。 ラッパクラスのクラス (Integer.class) を渡すと NoSuchMethodException が投げられます。 つまり、メソッド取得に関して Auto Boxing/Unboxing は行われません。 これはコンストラクタでも同じです。

内部クラス Inner Class

最後は内部クラス。 これらには、引数を指定して一意にメンバクラスを取得するメソッドはありません:

メソッド名 返り値
getClasses() Class<?>[]
getDeclaredClasses() Class<?>[]

サンプルコード

public class SuperClass{
    public static class SuperMemberClass {}
}

public class MyClass extends SuperClass{
    
    public static class MyMemberClass {}

    public static interface MyMemberInterface{}
    public static enum MyMemberEnum{}
    public static @interface MyMemberAnnotation{}
}

と定義すると以下のようになります:

    Class<?>[] types0 = MyClass.class.getClasses();
        // 順番は別にして
        //   [SuperClass.SuperMemberClass.class, 
        //    MyClass.MyMemberClass.class, 
        //    MyClass.MyMemberInterface.class, 
        //    MyClass.MyMemberEnum.class, 
        //    MyClass.MyMemberAnnotation.class] が返される

    Class<?>[] types1 = MyClass.class.getDeclaredClasses();
        // 順番は別にして
        //   [MyClass.MyMemberClass.class, 
        //    MyClass.MyMemberInterface.class, 
        //    MyClass.MyMemberEnum.class, 
        //    MyClass.MyMemberAnnotation.class] が返される

getClasses() となってますが、クラス内に定義されているインターフェースや列挙型、アノテーションも返します。 ちなみに getInterfaces() は実装しているインターフェースを返します。 念のため。

Java言語仕様 第3版 (The Java Series)

Java言語仕様 第3版 (The Java Series)

*1:同名で引数の個数や型が異なる

*2:コンストラクタが複数返されることを考えれば当然ですが。