[java] 두 개체의 심층 비교를 수행하는 Java 리플렉션 유틸리티가 있습니까?

clone()대규모 프로젝트 내 에서 다양한 작업에 대한 단위 테스트를 작성하려고하는데 같은 유형의 두 개체를 가져 와서 깊은 비교를 수행 할 수있는 기존 클래스가 어딘가에 있는지 궁금합니다. 똑같은가요?



답변

Unitils 에는 다음과 같은 기능이 있습니다.

Java 기본값 / 널 값 무시 및 컬렉션 순서 무시와 같은 다양한 옵션을 사용하여 리플렉션을 통한 평등 주장


답변

나는이 질문을 좋아한다! 주로 거의 대답하지 않았거나 나쁘게 대답하지 않았기 때문입니다. 아직 아무도 알아 내지 못한 것 같습니다. 버진 영토 🙂

우선,도없는 생각 사용에 대한 equals. 의 계약 equalsjavadoc 내에서 정의는 동치 관계 (재귀, 대칭 및 전이)입니다 하지 동등 관계. 이를 위해서는 비대칭이어야합니다. 그것의 유일한 구현은 equals진정한 평등 관계입니다 java.lang.Object. equals그래프의 모든 것을 비교 하는 데 사용 했더라도 계약을 위반할 위험이 상당히 높습니다. Josh Bloch가 Effective Java 에서 지적했듯이 동등 계약은 매우 쉽게 깨뜨릴 수 있습니다.

“균등 계약을 유지하면서 인스턴스화 가능한 클래스를 확장하고 측면을 추가 할 수있는 방법은 없습니다.”

어쨌든 부울 메서드가 실제로 무슨 소용이 있습니까? 원본과 복제품 사이의 모든 차이점을 실제로 캡슐화하는 것이 좋을 것 같지 않습니까? 또한 여기서는 그래프의 각 개체에 대한 비교 코드를 작성 / 유지하는 데 신경 쓰지 않고 시간이 지남에 따라 변경 될 때 소스에 따라 확장 될 무언가를 찾고 있다고 가정합니다.

정말 원하는 것은 일종의 상태 비교 도구입니다. 이 도구를 구현하는 방법은 실제로 도메인 모델의 특성과 성능 제한에 따라 다릅니다. 제 경험상 일반적인 마법 총알은 없습니다. 그리고 많은 반복에서 느려질 입니다. 그러나 복제 작업의 완전성을 테스트하기 위해서는 작업을 꽤 잘 수행 할 것입니다. 가장 좋은 두 가지 옵션은 직렬화와 반영입니다.

발생할 수있는 몇 가지 문제 :

  • 컬렉션 순서 : 두 컬렉션이 동일한 객체를 보유하지만 순서가 다른 경우 유사한 것으로 간주해야합니까?
  • 무시할 필드 : 일시적입니까? 공전?
  • 유형 동등성 : 필드 값이 정확히 동일한 유형이어야합니까? 아니면 하나가 다른 하나를 확장해도 괜찮습니까?
  • 더 있지만 잊어 버렸습니다 …

XStream은 매우 빠르며 XMLUnit과 결합하면 몇 줄의 코드만으로 작업을 수행 할 수 있습니다. XMLUnit은 모든 차이점을보고하거나 처음 발견 한 부분에서 멈출 수 있기 때문에 좋습니다. 출력에는 다른 노드에 대한 xpath가 포함되어 있습니다. 기본적으로 정렬되지 않은 컬렉션을 허용하지 않지만 그렇게하도록 구성 할 수 있습니다. 특별한 차이 핸들러 (라고 함 DifferenceListener)를 삽입 하면 순서 무시를 포함하여 차이를 처리하려는 방식을 지정할 수 있습니다. 그러나 가장 단순한 사용자 지정 이상의 작업을 수행하려는 즉시 작성하기가 어려워지고 세부 정보가 특정 도메인 개체에 묶여있는 경향이 있습니다.

개인적으로 선호하는 것은 리플렉션을 사용하여 선언 된 모든 필드를 순환하고 각 필드를 드릴 다운하여 이동하면서 차이점을 추적하는 것입니다. 경고 : 스택 오버플로 예외가 마음에 들지 않는 한 재귀를 사용하지 마십시오. 스택을 사용하여 범위 내에 유지 (사용LinkedList또는 뭔가). 나는 일반적으로 과도 및 정적 필드를 무시하고 이미 비교 한 객체 쌍을 건너 뛰기 때문에 누군가가 자기 참조 코드를 작성하기로 결정한 경우 무한 루프로 끝나지 않습니다 (그러나 나는 항상 기본 래퍼를 비교합니다. , 동일한 객체 참조가 종종 재사용되기 때문에). 컬렉션 순서를 무시하고 특수 유형 또는 필드를 무시하도록 미리 구성 할 수 있지만 주석을 통해 필드 자체에 대한 상태 비교 정책을 정의하고 싶습니다. IMHO는 런타임에 클래스에 대한 메타 데이터를 제공하기 위해 주석이 의미하는 바입니다. 다음과 같은 것 :


@StatePolicy(unordered=true, ignore=false, exactTypesOnly=true)
private List<StringyThing> _mylist;

나는 이것이 실제로 정말 어려운 문제라고 생각하지만 완전히 해결할 수 있습니다! 그리고 일단 당신에게 효과가있는 것이 있으면 정말, 정말, 편리합니다 🙂

그럼 행운을 빕니다. 그리고 순수한 천재적인 것을 생각해 내면 공유하는 것을 잊지 마십시오!


답변

java-util : https://github.com/jdereg/java-util 내의 DeepEquals 및 DeepHashCode ()를 참조하십시오.

이 클래스는 원래 작성자가 요청한 작업을 정확히 수행합니다.


답변

equals () 메서드 재정의

여기에 설명 된대로 EqualsBuilder.reflectionEquals () 를 사용하여 클래스 의 equals () 메서드를 간단히 재정의 할 수 있습니다 .

 public boolean equals(Object obj) {
   return EqualsBuilder.reflectionEquals(this, obj);
 }


답변

Hibernate Envers에 의해 수정 된 두 엔티티 인스턴스의 비교를 구현하기 만하면됩니다. 나는 내 자신의 다른 글을 쓰기 시작했지만 다음 프레임 워크를 발견했습니다.

https://github.com/SQiShER/java-object-diff

동일한 유형의 두 개체를 비교할 수 있으며 변경, 추가 및 제거가 표시됩니다. 변경 사항이 없으면 객체가 동일합니다 (이론상). 검사 중에 무시해야하는 게터에 대한 주석이 제공됩니다. 프레임 작업은 동등성 검사보다 훨씬 더 광범위한 응용 프로그램을 가지고 있습니다. 즉, 변경 로그를 생성하는 데 사용하고 있습니다.

성능은 괜찮습니다. JPA 엔티티를 비교할 때 먼저 엔티티 관리자에서 분리해야합니다.


답변

저는 XStream을 사용하고 있습니다.

/**
 * @see java.lang.Object#equals(java.lang.Object)
 */
@Override
public boolean equals(Object o) {
    XStream xstream = new XStream();
    String oxml = xstream.toXML(o);
    String myxml = xstream.toXML(this);

    return myxml.equals(oxml);
}

/**
 * @see java.lang.Object#hashCode()
 */
@Override
public int hashCode() {
    XStream xstream = new XStream();
    String myxml = xstream.toXML(this);
    return myxml.hashCode();
}


답변

에서 AssertJ , 당신은 할 수 있습니다 :

Assertions.assertThat(expectedObject).isEqualToComparingFieldByFieldRecursively(actualObject);

아마도 모든 경우에 작동하지는 않지만 생각할 수있는 더 많은 경우에 작동 할 것입니다.

문서 내용은 다음과 같습니다.

테스트중인 개체 (실제)가 속성 / 필드 비교 (상속 된 항목 포함)에 의한 속성 / 필드의 재귀 적 비교를 기반으로 주어진 개체와 동일하다는 것을 확인합니다. 실제와 같음 구현이 적합하지 않은 경우 유용 할 수 있습니다. 재귀 속성 / 필드 비교는 사용자 정의 같음 구현이있는 필드에 적용되지 않습니다. 즉, 필드 별 비교 대신 재정의 된 같음 메서드가 사용됩니다.

재귀 비교는주기를 처리합니다. 기본적으로 부동 소수점은 1.0E-6의 정밀도와 비교되고 1.0E-15의 두 배가됩니다.

각각 usingComparatorForFields (Comparator, String …) 및 usingComparatorForType (Comparator, Class)을 사용하여 (중첩 된) 필드 또는 유형별로 사용자 지정 비교기를 지정할 수 있습니다.

비교할 개체는 다른 유형일 수 있지만 동일한 속성 / 필드를 가져야합니다. 예를 들어 실제 객체에 이름 문자열 필드가있는 경우 다른 객체에도 하나가 있어야합니다. 개체에 이름이 같은 필드와 속성이있는 경우 속성 값이 필드 위에 사용됩니다.