技術メモ

技術メモ

ラフなメモ

Javaのリフレクションでフィールドを扱ってみる

リフレクションの勉強をします。今回はフィールドに関するリフレクションについて勉強します。

概要

フィールドに関するリフレクションは java.lang.reflect.Field にまとまっています。

Fieldは、クラスまたはインタフェースについての情報、それらへの動的なアクセス、その単一フィールドを提供します。リフレクトされたフィールドが、クラス(static)フィールドまたはインスタンス・フィールドであることもあります。

テストクラス

Foo.java

package jp.co.sample;

public class Foo {
    public String publicField;
    private String privateField;
    public Integer hoge;
    public Double fuga;

    static {
        System.out.println("static block.");
    }

    public Foo() {
        System.out.println("constructor.");
    }

    public Foo(String publicField, Integer hoge, Double fuga) {
        this.publicField = publicField;
        this.hoge = hoge;
        this.fuga = fuga;
    }

    protected Foo(String publicField) {
        this.publicField = publicField;
    }

    Foo(Integer hoge) {
        this.hoge = hoge;
    }

    private Foo(Double fuga) {
        this.fuga = fuga;
    }

    public String getBar() {
        return this.publicField;
    }

    public void setBar(String publicField) {
        this.publicField = publicField;
    }

    public int publicMethod() {
        return 1;
    }

    public int publicMethod(int num) {
        return 1 * num;
    }

    protected int protectedMethod() {
        return 2;
    }

    int defaultMethod() {
        return 3;
    }

    private int privateMethod() {
        return 4;
    }

    public static String staticMethod() {
        return "static";
    }

    @Override
    public String toString() {
        return "Foo [bar=" + publicField + "]";
    }
}

フィールドの取得

フィールドを取得するためのメソッドは以下があります。

メソッド 戻り値 取得可能なアクセス修飾子
Class#getField Field public
Class#getFields Field public
Class#getDeclaredField Field すべて
Class#getDeclaredFields Field すべて

フィールドの型を取得(Field#getType)

Class#getFieldField を取得して、Field#getType で型を取得します。getDeclaredField ではprivateなフィールドにアクセスすることができます。

    @Test
    public void getFieldでpublicフィールドの型が取得できること() throws Exception {
        Field field = Foo.class.getField("publicField");
        assertThat(field.getType(), is(equalTo(String.class)));
    }

    @Test(expected = java.lang.NoSuchFieldException.class)
    public void getFieldでprivateフィールドの型が取得できないこと() throws Exception {
        Field field = Foo.class.getField("privateField");
    }

    @Test
    public void getDeclaredFieldでpublicフィールドの型が取得できること() throws Exception {
        Field field = Foo.class.getDeclaredField("publicField");
        assertThat(field.getType(), is(equalTo(String.class)));
    }

    public void getDeclaredFieldでprivateフィールドの型が取得できること() throws Exception {
        Field field = Foo.class.getDeclaredField("privateField");
        assertThat(field.getType(), is(equalTo(String.class)));
    }

フィールドの名前を取得(Field#getName)

Field#getName でフィールド名を取得します。

    @Test
    public void getFieldでpublicフィールドの名前が取得できること() throws Exception {
        Field field = Foo.class.getField("publicField");
        assertThat(field.getName(), is("publicField"));
    }

フィールドの値を取得

Field#get でフィールドの値を取得します。

    @Test
    public void getでpublicフィールドの値が取得できること1() throws Exception {
        Foo foo = new Foo("public", 100, 0.11);
        Field field = foo.getClass().getField("publicField");
        assertThat(field.get(foo), is("public"));
    }

    @Test
    public void getでpublicフィールドの値が取得できること2() throws Exception {
        Foo foo = new Foo("public", 100, 0.11);
        Field field = foo.getClass().getField("hoge");
        assertThat(field.get(foo), is(100));
    }

    @Test
    public void getでpublicフィールドの値が取得できること3() throws Exception {
        Foo foo = new Foo("public", 100, 0.11);
        Field field = foo.getClass().getField("fuga");
        assertThat(field.get(foo), is(0.11));
    }

フィールドの値を設定

Field#set でフィールドの値を取得します。

privateなフィールドに関して、デフォルトのリフレクションではsetすることはできませんが、setAccessible(true) とするとsetできるようになります。これは AccessibleObject クラスの以下の仕様によるものです。

リフレクトされたオブジェクトでaccessibleフラグを設定すると、十分な特権を持つ高度なアプリケーション(Javaのオブジェクトの直列化やその他の持続性メカニズムなど)は、通常は禁止されている方法でオブジェクトを操作できます。デフォルトでは、リフレクトされたオブジェクトはアクセス可能ではありません。

    @Test
    public void setでpublicフィールドの名前がセットできること() throws Exception {
        Foo foo = new Foo("public", 100, 0.11);
        Field field = foo.getClass().getDeclaredField("publicField");
        field.set(foo, "change!");
        assertThat(field.get(foo), is("change!"));
    }

    @Test(expected = java.lang.IllegalAccessException.class)
    public void setでprivateフィールドの名前がセットできないこと() throws Exception {
        Foo foo = new Foo("public", 100, 0.11);
        Field field = foo.getClass().getDeclaredField("privateField");
        field.set(foo, "change!");
        assertThat(field.get(foo), is("change!"));
    }

    @Test
    public void setでprivateフィールドの名前がセットできること() throws Exception {
        Foo foo = new Foo("public", 100, 0.11);
        Field field = foo.getClass().getDeclaredField("privateField");
        field.setAccessible(true);
        field.set(foo, "change!");
        assertThat(field.get(foo), is("change!"));
    }