ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

クラスをよしなにデバッグ出力

いつもコメントいただいているmoto0215さんのクラスをよしなにデバッグ出力したいなに反応してみる。
一般的なやりかたは、クラスのtoStringメソッドを実装するというやり方でしょう。
■Main.java

package test;

public class Main
{
    public static void main(String[] args)
    {
        Test t = new Test();
        t.setNumber(123);
        t.setStr("abc");
        
        System.out.println(t);
    }
}

■Test.java

package test;

public class Test {
        private int number;
        private String str;
        
        public int getNumber() {
            return number;
        }
        
        public void setNumber(int number) {
            this.number = number;
        }
        
        public String getStr() {
            return str;
        }
        
        public void setStr(String str) {
            this.str = str;
        }

        public String toString()
        {
            return "number = " + number + " str = " + str;
        }
}

■実行結果

number = 123 str = abc

というわけなのですが、これだと全クラス自分でtoStringを実装しなければならないので、結構面倒。
で、汎用的に出来そうなのは、commons-beanutilsだと思い、適当に調べてみるとありました。BeanUtils#describeというメソッドです。

■Main_2.java

package test;

import org.apache.commons.beanutils.BeanUtils;

public class Main_2 {
    public static void main(String[] args) throws Exception {
        Test t = new Test();
        t.setNumber(123);
        t.setStr("abc");

        System.out.println(BeanUtils.describe(t));
    }
}

■実行結果

{class=class test.Test, str=abc, number=123}

AbstractMap#toStringがあるので、いい感じに出ますね。
ついでに入れ子にしたらどうなるか。

■Parent.java

package test;

public class Parent {
    private int number;
    private Test test;

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public Test getTest() {
        return test;
    }

    public void setTest(Test test) {
        this.test = test;
    }
}

■Main_3.java

package test;

import org.apache.commons.beanutils.BeanUtils;

public class Main_3
{
    public static void main(String[] args) throws Exception
    {
        Test t = new Test();
        t.setNumber(123);
        t.setStr("abc");
        
        Parent p = new Parent();
        p.setNumber(234);
        p.setTest(t);
        
        System.out.println(BeanUtils.describe(p));
    }
}

■実行結果

{class=class test.Parent, test=number = 123 str = abc, number=234}

というわけで、testフィールドも展開されて表示されます。結構実用的ですね。
ところが、内部クラスに対してやってみると、エラーになってしまいます。なんででしょうね?

■Main_1.java

package test;

import org.apache.commons.beanutils.BeanUtils;

public class Main_1 {
    public static void main(String[] args) throws Exception {
        Test t = new Test();
        t.setNumber(123);
        t.setStr("abc");

        System.out.println(BeanUtils.describe(t));
    }
    
    static class Test {
        private int number;
        private String str;
        
        public int getNumber() {
            return number;
        }
        
        public void setNumber(int number) {
            this.number = number;
        }
        
        public String getStr() {
            return str;
        }
        
        public void setStr(String str) {
            this.str = str;
        }
    }
}

■実行結果

Exception in thread "main" java.lang.NoSuchMethodException: Property 'number' has no getter method
        at org.apache.commons.beanutils.PropertyUtilsBean.getSimpleProperty(PropertyUtilsBean.java:1127)
        at org.apache.commons.beanutils.PropertyUtilsBean.getNestedProperty(PropertyUtilsBean.java:686)
        at org.apache.commons.beanutils.BeanUtilsBean.getNestedProperty(BeanUtilsBean.java:698)
        at org.apache.commons.beanutils.BeanUtilsBean.getProperty(BeanUtilsBean.java:723)
        at org.apache.commons.beanutils.BeanUtilsBean.describe(BeanUtilsBean.java:504)
        at org.apache.commons.beanutils.BeanUtils.describe(BeanUtils.java:145)
        at test.Main_1.<init>(Main_1.java:15)
        at test.Main_1.main(Main_1.java:7)