Javaで開発してるときに、大きめなObjectの中身を確認したいなぁーと思い、
きれいに整形してくれるプリティプリントするライブラリを探してみたけど、
なかなかいいのがなかったので、自分で作った時の備忘録
作ったライブラリはこちら。
SetとかMapとかArarryとかは対応してないけど、いずれ。。MavenCentralにも公開できてないけど、いずれ。。。
表示のされ方はこんな感じ。
こんなBeanに対して、
@Setter @Getter public class Sample { private String str; private List<String> strList; private List<Sample> children; }
こんなインスタンスを作って、
Sample obj = new Sample(); obj.setStr("aaa"); obj.setStrList(Arrays.asList(new String[] { "AAA", "BBB", "CCC" })); Sample child1 = new Sample(); child1.setStr("child1"); child1.setChildren(Collections.emptyList()); Sample child2 = new Sample(); child2.setStr("child2"); obj.setChildren(Arrays.asList(new Sample[] { child1, child2 }));
こんな感じに呼び出すと、
System.out.println(PP4j.pp(obj));
こんな感じにインデントしてくれます。
Sample { str = aaa strList = [AAA, BBB, CCC] children = [ Sample { str = child1 strList = <null> children = [] } Sample { str = child2 strList = <null> children = <null> } Sample { str = <null> strList = <null> children = <null> } ] }
使ったリフレクションまとめ
Class<?>系
// java.lang.Classの取得 Class<?> cls = obj.getClass(); // ClassのFQCNの取得 String fqcn = cls.getName(); // Class名の取得 String className = cls.getSimpleName(); // Classに定義されているFieldを取得 Field[] fields = cls.getDeclaredFields(); // cls.getFields()もあるが、こっちはpublicなものしかとれない
Field系
// Fieldに対してアクセス可能に設定。これでprivateフィールドの値も取れる field.setAccessible(true); // フィールド名を取得 String fieldName = field.getName(); // オブジェクトのインスタンスから実際のFieldのインスタンスを取得 Object field = field.get(obj); // FieldのTypeを取得。ListなどGenericsの場合の判定に利用。 Type type = field.getType() // FieldがList<String>だった場合に、Stringの部分を取得する if (type == List.class) { // Listの場合、総称型を取得 Type gType = field.getGenericType(); if (gType instanceof ParameterizedType) { // 総称型だった場合、実際の型を取得 Type[] pType = ((ParameterizedType) gType).getActualTypeArguments(); // やっとここで、Stringである、pType[0]を取得できる。。。 } }
はまったところ。。。
クラス自体が総称型だった場合、実際の型の取得は、要素を確認しないとわからない。
なぜかクラス自体(Class<?>)は、実際の型パラメタの情報を持っていない。。。
なので、実際なんの型なのかは、要素を入る必要がある。
こんな感じだが、要素がnullだったりすると、もはやわからない。。
List<Sample> objList = new ArrayList<>(); objList.add(new Sample()); Object elm = objList.get(0); Class cls = elm.getClass(); if (cls == String.class) { }
Filedでも総称の型パラメタを調べるのがめんどう。。。
上記に書いているが、めんどくさい。。
Type自体の型階層がこんな感じ。
java.lang.reflect.Type ... インターフェース - java.lang.reflect.GenericArrayType ... サブインタ―フェース - java.lang.reflect.ParameterizedType ... サブインタ―フェース - java.lang.reflect.TypeVariable<D> ... サブインタ―フェース - java.lang.reflect.WildcardType ... サブインタ―フェース - java.lang.Class<T> ... 実装されたクラス
何故作ったか。Commons-LangとかCommons-BeanUtilsとかあるけど。。。
調べてみると、Commonsライブラリにあるらしい。
すこし触ってみると、ObjectのToStringをオーバライドしないといけなく、めんどくさい。。
表示も1行にまとまっていたり、中途半端に改行されたりとなんだかなぁと。。
もう好きに表示できるように作ればいいのかと思いたつ。
Apache Commons Langでの設定
全部のBeanのToStringをオーバライドする
public String toString() { return ToStringBuilder.reflectionToString(this); }
表示はこんな感じで1行になる。。。
sample.Sample@7ab2bfe1[str=aaa,strList=[AAA, BBB, CCC],children=[sample.Sample@6438a396[str=child1,strList=<null>,children=[]], sample.Sample@e2144e4[str=child2,strList=<null>,children=<null>]]]
Apache Commons BeanUtilsでの設定
こっちも全部のBeanのToStringを..(ry
public String toString() { return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE); }
改行はしてくれたけど、インデント。。。
sample.Sample@7ab2bfe1[ str=aaa strList=[AAA, BBB, CCC] children=[sample.Sample@6438a396[ str=child1 strList=<null> children=[] ], sample.Sample@e2144e4[ str=child2 strList=<null> children=<null> ]] ]