2013-10-31 1 views
1

리플렉션을 사용하여 객체의 필드를 검사하고 구조를 가로 질러 재귀 적으로 구조를 가로 지르는 작은 Java 프로그램을 만들었습니다. 디버거가 검사 할 때와 비슷하게 모든 멤버의 트리를 작성합니다 변하기 쉬운.오브젝트 hirearchy 변환시 방문 노드 표시

큰 문제는 순환 참조가 나타나는 경우입니다. 내 나무가 그래프가됩니다! 그럼 내 프로그램은 무한 루프에 입력하고 결국 StackOverflow 예외를 던졌습니다 (오, 아이러니!)

내 질문은 ... 어떻게 임의의 표준 그래프 transversal 알고리즘을 구현하기 위해 방문한 임의의 개체를 "표시"할 수 있습니까? 모든 객체가 입력으로 사용될 수 있고 다른 객체가 동일한 해시 코드를 반환 할 수 있기 때문에 hashCode()를 사용할 수 없습니다. 어떤 단서?

고맙습니다.

public class ClassHierarchyItem { 

private boolean parent; 
private String id; 
private String parentId; 
private String name; 
private String type; 
private String value; 

public ClassHierarchyItem(boolean parent, String id, String parentId, String name, String type, String value){ 
    this.parent = parent; 
    this.id = id; 
    this.parentId = parentId; 
    this.name = name; 
    this.type = type; 
    this.value = value; 
} 

public String toString() { 
    return (isParent() ? "+" : "") + name + " - " + type + " - " + value + " [id=" + id + ", pId=" + parentId + "]"; 
} 
//Getters and setters follow (cutted) 
} 

public class ClassHierarchyNavigator { 

public static void main(String[] args) { 
    Integer i = 123; 

    // Fails with StackOverflow exception (some reference inside Integer points back to base object) 
    System.out.println(renderHirearchy(i)); 
} 

public static List<ClassHierarchyItem> renderHirearchy(Object o) { 

    List<ClassHierarchyItem> items = new ArrayList<ClassHierarchyItem>(); 

    boolean parent = (o.getClass().getDeclaredFields().length > 0 && !o.getClass().isPrimitive() && o.getClass() != String.class) 
      || o.getClass().isArray(); 

    buildObjectTree(items, o, parent, "root", o.getClass().getName(), "r", ""); 

    return items; 
} 

private static boolean isParent(Field field) { 

    return (field.getClass().getDeclaredFields().length > 0 && !field.getType().isPrimitive() && field.getType() != String.class) 
      || field.getType().isArray(); 
} 

private static void buildObjectTree(List<ClassHierarchyItem> items, Object object, boolean parent, 
     String objectName, String objectType, String objectId, String parentId) { 

    long subItemCount = 1; 
    String value = object == null ? "null" : object.toString(); 

    ClassHierarchyItem item = new ClassHierarchyItem(parent, objectId, parentId, objectName, objectType, 
      value.substring(0, value.length() > 80 ? 80 : value.length())); 
    items.add(item); 

    if (!parent) { 
     return; 
    } 

    // if (isArray) { 
    // do_array_treatment 
    // } else { 
    for (Field field : object.getClass().getDeclaredFields()) { 
     field.setAccessible(true); 
     Object child; 
     try { 
      child = field.get(object); 
     } catch (IllegalArgumentException e) { 
      continue; 
     } catch (IllegalAccessException e) { 
      continue; 
     } 

     String childId = objectId + "-" + subItemCount++; 
     String fieldName = field.getName(); 
     boolean childIsParent = child != null && !"this$0".equals(fieldName) && isParent(field); 

     buildObjectTree(items, child, childIsParent, fieldName, field.getType().getName(), childId, objectId); 
    } 

} 
} 
+2

검사 된 필드를 저장하는 곳의 코드입니다. –

+0

있습니다. 코드를 업로드했습니다. 이것은 단지 사전 알파 테스트 버전입니다! 빠르고 지저분한 코드입니다! – shandrio

답변

0

HashMap과 같은 해시 테이블 데이터 구조를 사용하고 해당 개체를 키로 사용하십시오. 실제로 일부 해시는 충돌하지만 충돌 해시는 해시 테이블의 근본 아이디어입니다. 모든 해시는 동일한 해시가있는 모든 키/값 쌍을 포함하는 버킷으로 연결되므로 개체를 검색 할 수 있습니다.

그러나 객체가 equals()를 다시 구현하여 두 객체가 같을 수 있다면 충돌 해결 방법으로 두 키를 동일한 버킷에서 구분할 수 없으므로 문제가됩니다. 그렇다면 hashcode()를 사용하여 해시를 얻고 "=="비교를 사용하여 메모리 주소별로 객체를 비교하여 자신 만의 해설을 디자인하십시오.

+0

답장을 보내 주셔서 감사합니다.하지만 통과 할 수있는 객체가 어떤 유형이든 상관 없으므로 그렇게 할 수 없습니다! 내 (더러운) 코드를 보면 방금 원래 게시물을 업데이트했습니다. – shandrio