[Java] ToStringBuilder

ToStringBuilder란

  • org.apache.commons.lang.builder에 있는 클래스
  • toString()과 같이 reflection을 위한 구현을 편하게 할 수 있는 클래스
  • 객체의 세부적인 내용들(filed명, value등)을 String으로 출력해 준다.
  • 객체, 컬랙션의 내용을 여러 style로 또는 새로운 style로 출력 할 수 있다.

Import

pom.xml에

<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>{version}</version>
</dependency>

or
commons-lang-{version}.jar를 추가한다.

Base Sample Example

append()를 이용한 예제
  • ToStringBuilder 객체를 생성한 후 append 함수에 필드명과 값(또는 값만)을 입력하면 된다.
  • append는 거의 모든 파라미터 타입을 오버로딩하고 있다.
  • ToStringBuilder는 내부적으로 StringBuffer를 이용하여 정해진 Style 형식에 맞춰 값들을 출력해 준다.
  • javaScore에는 값을 넣지 않았다.
public class Member {
  private String memberId;
  private int age;
  private boolean rookie;
  private List<String> projectList;
  private int[] javaScore;

        /** getter,setter method **/

  public String toString() {
    return new ToStringBuilder(this).append("memberId", memberId).append("age", age).append(....).toString();
  }
}

com.nhnent.vo.Member@27f8302d[memberId=ne11238,age=26,rookie=true,project=[nVote, comico],javaScore=<null>]
reflectionToString()을 이용한 예제
  • 하나하나 append에 필드명과 값을 쓰는것은 매우 불편하다. 그래서 reflectionToString이란 static method를 제공한다.
public class Member {
...
public String toString() {
    return ToStringBuilder.reflectionToString(this).toString();
  }
}
  • static method 이기 때문에 외부에서 reflection도 가능하다
System.out.println(ToStringBuilder.reflectionToString(me).toString());
com.nhnent.vo.Member@27f8302d[memberId=ne11238,age=26,rookie=true,project=[nVote, comico],javaScore=<null>]

More Example

Style 설정
  • 기본 출력 형식 뿐만 아니라 여러 유형의 style설정이 가능하다.
  • ToStringBuilder는 기본적으로 5개의 style을 제공해준다.
ToStringBuilder.reflectionToString(me,ToStringStyle.스타일명).toString()
/** DefaultToStringStyle(기본) **/

/** NoFieldNameToStringStyle  **/
com.nhnent.vo.Member@2d8e6db6[ne11238,26,true,[nVote, comico],<null>]

/** ShortPrefixToStringStyle  **/
Member[memberId=ne11238,age=26,rookie=true,projectList=[nVote, comico],javaScore=<null>]

/** SimpleToStringStyle  **/
ne11238,26,true,[nVote, comico],<null>

/** MultiLineToStringStyle  **/
com.nhnent.vo.Member@2d8e6db6[
  memberId=ne11238
  age=26
  rookie=true
  projectList=[nVote, comico]
  javaScore=<null>
]
CustomStlye 만들기
  • object를 json형식으로 출력해주는 customstyle
  • ToStringStyle을 상속받아 구현한다.
public class JsonToStringStyle extends ToStringStyle {

  private static final long serialVersionUID = 1L;
  public static final ToStringStyle JSON_STYLE = new JsonToStringStyle();
  /**
   * <p>Constructor.</p>
   *
   * <p>Use the static constant rather than instantiating.</p>
   */
  JsonToStringStyle() {
    super();
    this.setUseClassName(false);
    this.setUseIdentityHashCode(false);
    this.setContentStart("{" + SystemUtils.LINE_SEPARATOR + "\t");
    this.setFieldNameValueSeparator(SystemUtils.PATH_SEPARATOR);
    this.setNullText("");
    this.setFieldSeparator("," + SystemUtils.LINE_SEPARATOR + "\t");
    this.setContentEnd(SystemUtils.LINE_SEPARATOR + "}");
  }

  /**
   * <p>Ensure <code>Singleton</code> after serialization.</p>
   *
   * @return the singleton
   */
  private Object readResolve() {
    return JsonToStringStyle.JSON_STYLE;
  }


}

{
  memberId:ne11238,
  age:26,
  rookie:true,
  projectList:[nVote, comico],
  javaScore:
}

구조

ToStringBuilder.Class
  • ToStringBuilder는 내부적으로 stringBuffer를 이용해 값을 저장한다.
  • toString() 메소드에서 값을 출력하고 append 메소드에서 stringBuffer을 ToStringstyle의 append()함수에 넘긴다.
 private final ToStringStyle style;
private final StringBuffer buffer;
.....
 public ToStringBuilder append(String fieldName, int value) {
        style.append(buffer, fieldName, value);
        return this;
    }
....
 public ToStringBuilder append(String fieldName, Object[] array, boolean fullDetail) {
        style.append(buffer, fieldName, array, BooleanUtils.toBooleanObject(fullDetail));
        return this;
    }
ToStringStyle.Class
  • ToStringStyle는 출력 로직을 구현하는 클래스이다.
/**ToStringBuilder.class 생성자에서 style 지정 **/
public ToStringBuilder(Object object, ToStringStyle style) {
        this(object, style, null);
    }
  • 지정한 Style에 맞추어서 append() 함수가 호출되면 Style변수에 지정되어 있는 ToStringStyle 구현 클래스로 값을 출력하도록 변환해준다.
  • 파라미터 종류에 따라서 appendFieldStart(), appendNullText(), appendFieldEnd() 등을 바꿔 가며 호출한다.
/** 파라미터에 따라서 다른 로직함수 호출 **/
public void append(StringBuffer buffer, String fieldName, int value) {
        appendFieldStart(buffer, fieldName);
        appendDetail(buffer, fieldName, value);
        appendFieldEnd(buffer, fieldName);
    }
....
public void append(StringBuffer buffer, String fieldName, Object[] array, Boolean fullDetail) {
        appendFieldStart(buffer, fieldName);

        if (array == null) {
            appendNullText(buffer, fieldName);

        } else if (isFullDetail(fullDetail)) {
            appendDetail(buffer, fieldName, array);

        } else {
            appendSummary(buffer, fieldName, array);
        }

        appendFieldEnd(buffer, fieldName);
    }
....
/** 출력 로직 함수 중 하나 **/
protected void appendDetail(StringBuffer buffer, String fieldName, Object[] array) {
        buffer.append(arrayStart);
        for (int i = 0; i < array.length; i++) {
            Object item = array[i];
            if (i > 0) {
                buffer.append(arraySeparator);
            }
            if (item == null) {
                appendNullText(buffer, fieldName);

            } else {
                appendInternal(buffer, fieldName, item, arrayContentDetail);
            }
        }
        buffer.append(arrayEnd);
    }
ReflectionToStringBuilder.Class
  • ReflectionToStringBuilder는 ToStringBuilder를 상속받아서 내부 객체를 검사하여 append()를 호출해주도록 구현
  • ReflectionToStringBuilder의 toString() 메소드에서 메소드를 호출한 객체의 object를 appendFieldsIn() 함수에 전달하고 이곳에서 class의 필드와 값을 얻어와서 append를 한 후 상위객체(ToStringbuilder)의 toString() 메소드로 넘겨준다.
  • 무한루프를 피하기 위해서 ToStringStyle에서는 ThreadLocal을 사용해 registry를 관리한다.
/** 객체를 검사하여 append함수 호출 **/
public class ReflectionToStringBuilder extends ToStringBuilder {
public String toString() {
        if (this.getObject() == null) {
            return this.getStyle().getNullText();
        }
        Class clazz = this.getObject().getClass();
        this.appendFieldsIn(clazz);
        while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) {
            clazz = clazz.getSuperclass();
            this.appendFieldsIn(clazz);
        }
        return super.toString();
    }
protected void appendFieldsIn(Class clazz) {
        if (clazz.isArray()) {
            this.reflectionAppendArray(this.getObject());
            return;
        }
        Field[] fields = clazz.getDeclaredFields();
        AccessibleObject.setAccessible(fields, true);
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            String fieldName = field.getName();
            if (this.accept(field)) {
                try {
                    // Warning: Field.get(Object) creates wrappers objects

                    // for primitive types.

                    Object fieldValue = this.getValue(field);
                    *this.append(fieldName, fieldValue);* 
                } catch (IllegalAccessException ex) {
                    //this can't happen. Would get a Security exception

                    // instead

                    //throw a runtime exception in case the impossible

                    // happens.

                    throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
                }
            }
        }
    }
}
public String toString() {
        if (this.getObject() == null) {
            this.getStringBuffer().append(this.getStyle().getNullText());
        } else {
            style.appendEnd(this.getStringBuffer(), this.getObject());
        }
        return this.getStringBuffer().toString();
    }

동작원리 및 시나리오

  • 사용자가 new ToStringBuilder().append("필드명",값)을 한다.
  • ToStringBuilder Class 내부에서 각 파라미터 타입에 맞는 append 함수를 호출된다
  • ToStringBuilder append() 에서는 각 경우에 따라 ToStringStyle의 append() 함수를 호출하여 내부의 stringbuffer와 값을 파라미터로 넘겨준다.
  • ToStringStyle의 append()는 각 파라미터의 타입과 경우, style등을 고려하여 buffer에 조합한 결과을 저장한다.
  • ToStringStyle의 출력형식은 자식클래스에서 지정하고 style인자 또한 ToStringBuilder에서 받는다.
  • 마지막으로 ToStringBuilder Class는 buffer에 저장된 내용을 toString() 함수에서 반환한다.

결론

  • ToStringBuilder을 쓰면 reflection을 쉽게 할 수 있다.
  • ReflectionToStringBuilder로 구현하는게 가장 편하다.
  • custom 스타일을 만들 수 있다.

Reference

http://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/builder/ToStringBuilder.html

Comments

comments powered by Disqus
comments powered by Disqus