情報科学屋さんを目指す人のメモ

方法・手順・解説を書き残すブログ。私と同じことを繰り返さずに済むように。

Java:クラスの全フィールドの名前と値の一覧を出力する方法

Java (38)

出力結果に、実行時の設定(field)を一緒に表示するために、Reflection機能を使って、全てのfield名とその値を表示するコードを書いたので、備考と共にメモしておきます。

設定内容が詰まっていたりするオブジェクトの中身をどばっと書き出しておきたいときに使っています。

コード

例えばこんなコードで出力できます(手元で動けばOKという感じで、特別な事情は考えていません)。

@Override
public String toString() {
	StringBuilder sb = new StringBuilder();
	sb.append("Class: " + this.getClass().getCanonicalName() + "\n");
	sb.append("Settings:\n");
	for (Field field : this.getClass().getDeclaredFields()) {
		try {
			field.setAccessible(true);
			sb.append(field.getName() + " = " + field.get(this) + "\n");
		} catch (IllegalAccessException e) {
			sb.append(field.getName() + " = " + "access denied\n");
		}
	}
	return sb.toString();
}

出力結果

こんな雰囲気で出力されます。

Class: net.did2memo.example.Example
Settings:
fieldA = 0
fieldB = 1000

説明

重要なのは「for文(拡張for文)」のところです。まず「this.getClass()」で、このクラスのクラス(actual type)である「Classオブジェクト」を取得しています。それに対して「.getDeclaredFields()」を利用して、fieldの一覧を取得しています。

getFields と getDeclaredFields の違い

getFieldsとgetDeclaredFieldsはどちらもオブジェクト自分自身のfieldを取得するのですが、getFieldsが取得できるのは、このコードが書かれたクラスからアクセス可能(アクセス修飾子的な意味で)なフィールドのみです。

これに対して、getDeclaredFieldsは、オブジェクトに実装されている全てのfieldを取得してくれます(※しかし、アクセス権が無いものも含まれるのがポイント)。

Field#getメソッドの使い方

Fieldクラスにはgetメソッドがあり、いかにもフィールドの値を取得できそうです。

しかし、なぜ「get()」ではなく「get(Object obj)」なのかというと、「this.getClass().getDeclaredFields()」で取得した「Field」オブジェクトは、もはや「this」という具体的なオブジェクトとは関係のないフィールドそのものを表すオブジェクトだからです。

というわけで、具体的にFieldの値を取得する対象をobjで指定してあげるわけです。ということで、今回は「this」を与えています。

try文の理由

try文が必要なのは、「Fieldクラスのgetメソッド」を呼び出すときに「IllegalAccessException」という例外が返る可能性があるためです。

どんなときに発生する例外かというと、アクセスできないフィールドに対してgetメソッドを呼んだ場合に発生します。

※これの対策で「sb.append(field.getName() + " = " + "accecc denied\n");」という文を追加したのですが、最終的にあまり意味のない感じになっています。

親クラスからも子クラスのprivateフィールドの値を取得したい

説明を飛ばした「field.setAccessible(true)」は、無くても動作します。というか、この行を最後に追加しました。なぜかというと、この実装を親クラスに書いて、すべての子クラスのオブジェクトで利用したかったのですが、子クラスのprivateフィールドを出力できなかったのです

通常のプログラミング時、親クラスから、子クラスのprivateフィールドは見えません。ここでもそのアクセス制限が効いてくるのです。

しかし、この「setAccessible(true)」により、あっさりアクセス可能になり、getメソッドは例外を返すことなく成功します(セキュリティの設定によっては不可能で、その場合は別の手段があるらしい)。

こうして、親クラスにこのコードを書いても、子クラスのprivateフィールドまで表示できるようになります。

おまけ:妙なフィールド?

このコードを実行すると、無いフィールドが表示されることがあります。とはいっても、本当に無いわけではなく、ユーザが書いたfieldとは別に自動的に作成される、普段は見えないフィールドが表示されるだけです。

例えば、switch文をメソッド内で使っていると、「$SWITCH_TABLE$」で始まるフィールドが作られます。また、アサーションを使っていると、「$assertionsDisabled」というフィールドも表示されます。今回のコードでは、これの出力を特に禁止するように書いていないため、これらは出力されてしまいます。注意。

コメント(0)

新しいコメントを投稿