ฉันกำลังพยายามเขียนการทดสอบหน่วยสำหรับการclone()
ดำเนินการที่หลากหลายภายในโปรเจ็กต์ขนาดใหญ่และฉันสงสัยว่ามีคลาสที่มีอยู่ที่ไหนสักแห่งที่สามารถรับวัตถุประเภทเดียวกันสองชิ้นทำการเปรียบเทียบอย่างลึกซึ้งและบอกว่าพวกเขา 'เหมือนกันหรือไม่?
ฉันกำลังพยายามเขียนการทดสอบหน่วยสำหรับการclone()
ดำเนินการที่หลากหลายภายในโปรเจ็กต์ขนาดใหญ่และฉันสงสัยว่ามีคลาสที่มีอยู่ที่ไหนสักแห่งที่สามารถรับวัตถุประเภทเดียวกันสองชิ้นทำการเปรียบเทียบอย่างลึกซึ้งและบอกว่าพวกเขา 'เหมือนกันหรือไม่?
คำตอบ:
Unitilsมีฟังก์ชันนี้:
การยืนยันความเท่าเทียมกันผ่านการสะท้อนด้วยตัวเลือกต่างๆเช่นการละเว้นค่าเริ่มต้น / ค่าว่างของ Java และละเว้นลำดับของคอลเลกชัน
unitils
นั้นมีข้อบกพร่องอย่างแม่นยำเพราะมันเปรียบเทียบตัวแปรแม้ว่ามันอาจไม่มีผลกระทบที่สังเกตได้ก็ตาม ผลที่ตามมาอีกประการหนึ่ง (ที่ไม่พึงปรารถนา) ของการเปรียบเทียบตัวแปรคือไม่รองรับการปิดอย่างแท้จริง (โดยไม่มีสถานะเป็นของตัวเอง) นอกจากนี้ยังต้องการให้วัตถุที่เปรียบเทียบเป็นประเภทรันไทม์เดียวกัน ฉันรีดแขนเสื้อและสร้างเครื่องมือเปรียบเทียบเชิงลึกในเวอร์ชันของตัวเองซึ่งช่วยแก้ปัญหาเหล่านี้
ฉันชอบคำถามนี้! ส่วนใหญ่เป็นเพราะแทบไม่เคยตอบหรือตอบไม่ดี มันเหมือนกับว่ายังไม่มีใครคิดออก ดินแดนเวอร์จิน :)
ปิดแรกไม่ได้คิดequals
เกี่ยวกับการใช้ สัญญาของequals
ตามที่กำหนดไว้ใน javadoc คือความสัมพันธ์ที่เท่าเทียมกัน (รีเฟล็กซีฟสมมาตรและสกรรมกริยา) ไม่ใช่ความสัมพันธ์แบบเท่าเทียมกัน สำหรับสิ่งนั้นก็จะต้องมีการป้องกันเสียงรบกวนด้วย การดำเนินการเฉพาะของequals
ที่มีอยู่ (หรือที่เคยอาจจะ) java.lang.Object
ความสัมพันธ์ที่เท่าเทียมกันจริงเป็นหนึ่งใน แม้ว่าคุณจะใช้equals
เพื่อเปรียบเทียบทุกอย่างในกราฟ แต่ความเสี่ยงในการผิดสัญญาก็ค่อนข้างสูง ดังที่ Josh Bloch ชี้ให้เห็นในภาษา Java ที่มีประสิทธิภาพการทำสัญญาเท่ากับนั้นง่ายมากที่จะทำลาย:
"ไม่มีวิธีใดเลยที่จะขยายคลาสแบบทันทีและเพิ่มแง่มุมในขณะที่รักษาสัญญาที่เท่าเทียมกัน"
นอกจากวิธีบูลีนที่ดีแล้วคุณยังทำอะไรได้อีก? มันเป็นการดีที่จะสรุปความแตกต่างทั้งหมดระหว่างต้นฉบับและโคลนคุณไม่คิดเหรอ? นอกจากนี้ฉันจะสมมติว่าคุณไม่ต้องการที่จะต้องกังวลกับการเขียน / การดูแลรักษาโค้ดเปรียบเทียบสำหรับแต่ละวัตถุในกราฟ แต่คุณกำลังมองหาบางสิ่งที่จะปรับขนาดตามแหล่งที่มาเมื่อมีการเปลี่ยนแปลงอยู่ตลอดเวลา
ดังนั้นสิ่งที่คุณต้องการจริงๆคือเครื่องมือเปรียบเทียบสถานะบางประเภท วิธีการใช้งานเครื่องมือนั้นขึ้นอยู่กับลักษณะของโมเดลโดเมนและข้อ จำกัด ด้านประสิทธิภาพของคุณ จากประสบการณ์ของฉันไม่มีกระสุนวิเศษทั่วไป และจะช้ากว่าการทำซ้ำจำนวนมาก แต่สำหรับการทดสอบความสมบูรณ์ของการดำเนินการโคลนมันจะทำงานได้ดี สองตัวเลือกที่ดีที่สุดของคุณคือการทำให้เป็นอนุกรมและการสะท้อนกลับ
ปัญหาบางอย่างที่คุณจะพบ:
XStream ค่อนข้างเร็วและเมื่อรวมกับ XMLUnit จะทำงานได้ในโค้ดเพียงไม่กี่บรรทัด XMLUnit เป็นสิ่งที่ดีเพราะสามารถรายงานความแตกต่างทั้งหมดหรือหยุดที่ความแตกต่างแรกที่พบ และเอาต์พุตของมันรวม xpath ไปยังโหนดที่แตกต่างกันซึ่งเป็นสิ่งที่ดี โดยค่าเริ่มต้นจะไม่อนุญาตให้มีคอลเล็กชันที่ไม่ได้เรียงลำดับ แต่สามารถกำหนดค่าให้ทำได้ การใส่ตัวจัดการความแตกต่างพิเศษ (เรียกว่า a DifferenceListener
) ช่วยให้คุณสามารถระบุวิธีที่คุณต้องการจัดการกับความแตกต่างรวมถึงการละเว้นลำดับ อย่างไรก็ตามทันทีที่คุณต้องการทำอะไรที่นอกเหนือจากการปรับแต่งที่ง่ายที่สุดการเขียนจะกลายเป็นเรื่องยากและรายละเอียดมักจะผูกติดกับวัตถุโดเมนเฉพาะ
ความชอบส่วนตัวของฉันคือการใช้การไตร่ตรองเพื่อวนรอบฟิลด์ที่ประกาศทั้งหมดและเจาะลึกลงไปในแต่ละฟิลด์ติดตามความแตกต่างในขณะที่ฉันไป คำเตือน: อย่าใช้การเรียกซ้ำเว้นแต่คุณจะชอบข้อยกเว้นสแตกล้น เก็บสิ่งของไว้ในขอบเขตด้วยสแต็ก (ใช้ไฟล์LinkedList
หรือบางสิ่งบางอย่าง). ฉันมักจะมองข้ามเขตข้อมูลชั่วคราวและแบบคงที่และฉันข้ามคู่วัตถุที่ฉันเคยเปรียบเทียบไปแล้วดังนั้นฉันจะไม่จบลงในลูปที่ไม่มีที่สิ้นสุดหากมีคนตัดสินใจเขียนรหัสอ้างอิงตัวเอง (อย่างไรก็ตามฉันมักจะเปรียบเทียบการห่อแบบดั้งเดิมไม่ว่าจะเกิดอะไรขึ้นก็ตาม เนื่องจากการอ้างอิงวัตถุเดียวกันมักจะถูกนำมาใช้ใหม่) คุณสามารถกำหนดค่าสิ่งต่างๆล่วงหน้าเพื่อละเว้นการสั่งซื้อคอลเลกชันและละเว้นประเภทหรือฟิลด์พิเศษ แต่ฉันต้องการกำหนดนโยบายการเปรียบเทียบสถานะของฉันในฟิลด์ด้วยตัวเองผ่านคำอธิบายประกอบ IMHO นี่คือความหมายของคำอธิบายประกอบเพื่อสร้างข้อมูลเมตาเกี่ยวกับคลาสที่พร้อมใช้งานในรันไทม์ สิ่งที่ต้องการ:
@StatePolicy(unordered=true, ignore=false, exactTypesOnly=true)
private List<StringyThing> _mylist;
ฉันคิดว่านี่เป็นปัญหาที่หนักมาก แต่ก็แก้ไขได้ทั้งหมด! และเมื่อคุณมีสิ่งที่เหมาะกับคุณแล้วมันก็มีประโยชน์จริงๆ :)
ดังนั้นขอให้โชคดี และหากคุณได้พบกับสิ่งที่เป็นอัจฉริยะเพียงอย่างเดียวอย่าลืมแบ่งปัน!
ดู DeepEquals และ DeepHashCode () ภายใน java-util: https://github.com/jdereg/java-util
คลาสนี้ทำตามที่ผู้เขียนดั้งเดิมร้องขอทุกประการ
public
ควรใช้กับทุกประเภทแทนที่จะใช้กับคอลเล็กชัน / แผนที่เท่านั้น
คุณสามารถลบล้างเมธอดequals ()ของคลาสโดยใช้EqualsBuilder.reflectionEquals ()ตามที่อธิบายไว้ที่นี่ :
public boolean equals(Object obj) {
return EqualsBuilder.reflectionEquals(this, obj);
}
ในAssertJคุณสามารถทำได้:
Assertions.assertThat(expectedObject).isEqualToComparingFieldByFieldRecursively(actualObject);
อาจจะใช้ไม่ได้ในทุกกรณี แต่จะใช้ได้ผลในหลาย ๆ กรณีที่คุณคิด
นี่คือสิ่งที่เอกสารระบุ:
ยืนยันว่าวัตถุที่อยู่ภายใต้การทดสอบ (จริง) เท่ากับวัตถุที่กำหนดโดยยึดตามคุณสมบัติ / เขตข้อมูลซ้ำโดยการเปรียบเทียบคุณสมบัติ / ฟิลด์ (รวมถึงวัตถุที่สืบทอด) สิ่งนี้จะมีประโยชน์หากการนำไปใช้งานจริงไม่เหมาะกับคุณ การเปรียบเทียบคุณสมบัติ / ฟิลด์แบบเรียกซ้ำจะไม่ถูกนำไปใช้กับฟิลด์ที่มีการใช้งานเท่ากับแบบกำหนดเองกล่าวคือเมธอดเท่ากับที่ถูกแทนที่จะถูกใช้แทนฟิลด์โดยการเปรียบเทียบฟิลด์
การเปรียบเทียบแบบวนซ้ำจะจัดการกับวัฏจักร โดยค่าเริ่มต้นการลอยจะถูกเปรียบเทียบด้วยความแม่นยำ 1.0E-6 และเพิ่มเป็นสองเท่าด้วย 1.0E-15
คุณสามารถระบุตัวเปรียบเทียบแบบกำหนดเองต่อฟิลด์ (ซ้อนกัน) หรือพิมพ์ตามลำดับโดยใช้ ComparatorForFields (ตัวเปรียบเทียบสตริง ... ) และการใช้ComparatorForType (ตัวเปรียบเทียบคลาส)
ออบเจ็กต์ที่จะเปรียบเทียบอาจเป็นประเภทต่างๆ แต่ต้องมีคุณสมบัติ / ฟิลด์เดียวกัน ตัวอย่างเช่นถ้าออบเจ็กต์จริงมีฟิลด์ชื่อสตริงก็คาดว่าอีกอ็อบเจกต์จะมีด้วย หากออบเจ็กต์มีฟิลด์และคุณสมบัติที่มีชื่อเดียวกันค่าคุณสมบัติจะถูกใช้เหนือฟิลด์
isEqualToComparingFieldByFieldRecursively
เลิกใช้งานแล้ว ใช้assertThat(expectedObject).usingRecursiveComparison().isEqualTo(actualObject);
แทน :)
ต้องใช้การเปรียบเทียบอินสแตนซ์เอนทิตีสองรายการที่แก้ไขโดย Hibernate Envers ฉันเริ่มเขียนความแตกต่างของตัวเอง แต่พบกรอบต่อไปนี้
https://github.com/SQiShER/java-object-diff
คุณสามารถเปรียบเทียบออบเจ็กต์สองชิ้นที่มีประเภทเดียวกันและจะแสดงการเปลี่ยนแปลงการเพิ่มเติมและการนำออก หากไม่มีการเปลี่ยนแปลงแสดงว่าวัตถุมีค่าเท่ากัน (ตามทฤษฎี) คำอธิบายประกอบมีไว้สำหรับ getters ที่ควรละเว้นในระหว่างการตรวจสอบ งานเฟรมมีแอพพลิเคชั่นที่กว้างกว่าการตรวจสอบความเท่าเทียมกันนั่นคือฉันกำลังใช้เพื่อสร้างบันทึกการเปลี่ยนแปลง
ประสิทธิภาพของมันอยู่ในเกณฑ์ดีเมื่อเปรียบเทียบเอนทิตี 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();
}
http://www.unitils.org/tutorial-reflectionassert.html
public class User {
private long id;
private String first;
private String last;
public User(long id, String first, String last) {
this.id = id;
this.first = first;
this.last = last;
}
}
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);
Hamcrest มี Matcher samePropertyValuesAs แต่ต้องอาศัยอนุสัญญา JavaBeans (ใช้ getters และ setters) หากออบเจ็กต์ที่จะเปรียบเทียบไม่มีตัวรับและตัวเซ็ตสำหรับแอตทริบิวต์สิ่งนี้จะไม่ทำงาน
import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
import static org.junit.Assert.assertThat;
import org.junit.Test;
public class UserTest {
@Test
public void asfd() {
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertThat(user1, samePropertyValuesAs(user2)); // all good
user2 = new User(1, "John", "Do");
assertThat(user1, samePropertyValuesAs(user2)); // will fail
}
}
ถั่วผู้ใช้ - พร้อมตัวรับและตัวตั้งค่า
public class User {
private long id;
private String first;
private String last;
public User(long id, String first, String last) {
this.id = id;
this.first = first;
this.last = last;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
}
isFoo
วิธีการอ่านสำหรับBoolean
คุณสมบัติ มี PR ที่เปิดให้แก้ไขตั้งแต่ปี 2559 github.com/hamcrest/JavaHamcrest/pull/136
หากวัตถุของคุณใช้ Serializable คุณสามารถใช้สิ่งนี้:
public static boolean deepCompare(Object o1, Object o2) {
try {
ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
ObjectOutputStream oos1 = new ObjectOutputStream(baos1);
oos1.writeObject(o1);
oos1.close();
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
ObjectOutputStream oos2 = new ObjectOutputStream(baos2);
oos2.writeObject(o2);
oos2.close();
return Arrays.equals(baos1.toByteArray(), baos2.toByteArray());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
ตัวอย่างรายการที่เชื่อมโยงของคุณไม่ใช่เรื่องยากที่จะจัดการ ในขณะที่รหัสผ่านกราฟวัตถุทั้งสองกราฟจะวางวัตถุที่เยี่ยมชมไว้ในชุดหรือแผนที่ ก่อนที่จะข้ามไปยังการอ้างอิงวัตถุอื่นชุดนี้จะได้รับการทดสอบเพื่อดูว่าวัตถุนั้นถูกข้ามผ่านไปแล้วหรือไม่ ถ้าใช่ก็ไม่ต้องไปต่อ
ฉันเห็นด้วยกับบุคคลข้างต้นที่บอกว่าใช้ LinkedList (เหมือน Stack แต่ไม่มีวิธีการซิงโครไนซ์จึงเร็วกว่า) การข้ามกราฟวัตถุโดยใช้ Stack ในขณะที่ใช้การสะท้อนเพื่อให้ได้แต่ละฟิลด์เป็นทางออกที่ดี เขียนครั้งเดียว "ภายนอก" นี้เท่ากับ () และ "ภายนอก" hashCode () คือสิ่งที่ควรเรียกใช้เมธอด equals () และ hashCode () ทั้งหมด คุณไม่ต้องการวิธีการเท่ากับลูกค้า () อีกต่อไป
ฉันเขียนโค้ดเล็กน้อยที่ลากผ่านกราฟออบเจ็กต์ที่สมบูรณ์ซึ่งระบุไว้ใน Google Code ดู json-io (http://code.google.com/p/json-io/) มันทำให้กราฟออบเจ็กต์ Java เป็นอนุกรมเป็น JSON และแยกส่วนออกจากมัน มันจัดการกับวัตถุ Java ทั้งหมดโดยมีหรือไม่มีตัวสร้างสาธารณะ Serializeable หรือไม่ต่ออนุกรมได้ ฯลฯ รหัสการส่งผ่านเดียวกันนี้จะเป็นพื้นฐานสำหรับการใช้งานภายนอก "equals ()" และ "hashcode ()" ภายนอก Btw, JsonReader / JsonWriter (json-io) มักจะเร็วกว่า ObjectInputStream / ObjectOutputStream ในตัว
JsonReader / JsonWriter นี้สามารถใช้ในการเปรียบเทียบได้ แต่จะไม่ช่วยในเรื่องแฮชโค้ด หากคุณต้องการ hashcode สากล () และเท่ากับ () จำเป็นต้องมีรหัสของตัวเอง ฉันอาจสามารถดึงสิ่งนี้ออกได้ด้วยผู้เข้าชมกราฟทั่วไป เราจะเห็น
ข้อควรพิจารณาอื่น ๆ - ช่องแบบคงที่ซึ่งเป็นเรื่องง่าย - สามารถข้ามได้เนื่องจากอินสแตนซ์ equals () ทั้งหมดจะมีค่าเดียวกันสำหรับฟิลด์แบบคงที่เนื่องจากฟิลด์แบบคงที่จะใช้ร่วมกันในทุกอินสแตนซ์
สำหรับช่องชั่วคราว - นั่นจะเป็นตัวเลือกที่เลือกได้ บางครั้งคุณอาจต้องการชั่วคราวเพื่อนับเวลาอื่น ๆ "บางครั้งคุณรู้สึกเหมือนถั่ว
กลับไปที่โปรเจ็กต์ json-io (สำหรับโปรเจ็กต์อื่น ๆ ของฉัน) และคุณจะพบโปรเจ็กต์ equals () / hashcode () ภายนอก ฉันยังไม่มีชื่อ แต่จะเห็นได้ชัด
ฉันเดาว่าคุณรู้เรื่องนี้ แต่ในทางทฤษฎีคุณควรจะลบล้างเสมอเท่ากับยืนยันว่าวัตถุสองชิ้นมีค่าเท่ากันอย่างแท้จริง นี่หมายความว่าพวกเขาตรวจสอบเมธอด .equals ที่ถูกแทนที่บนสมาชิกของพวกเขา
สิ่งนี้คือสาเหตุที่ .equals ถูกกำหนดไว้ใน Object
หากสิ่งนี้ทำได้อย่างสม่ำเสมอคุณก็จะไม่มีปัญหา
การหยุดการรับประกันสำหรับการเปรียบเทียบที่ลึกซึ้งเช่นนี้อาจเป็นปัญหาได้ ต่อไปนี้ควรทำอย่างไร? (หากคุณใช้ตัวเปรียบเทียบดังกล่าวจะเป็นการทดสอบหน่วยที่ดี)
LinkedListNode a = new LinkedListNode();
a.next = a;
LinkedListNode b = new LinkedListNode();
b.next = b;
System.out.println(DeepCompare(a, b));
นี่คืออีก:
LinkedListNode c = new LinkedListNode();
LinkedListNode d = new LinkedListNode();
c.next = d;
d.next = c;
System.out.println(DeepCompare(c, d));
Using an answer instead of a comment to get a longer limit and better formatting.
ถ้าเป็นความคิดเห็นแล้วทำไมต้องใช้ส่วนคำตอบ? นั่นคือเหตุผลที่ฉันตั้งค่าสถานะไว้ ไม่ใช่เพราะไฟล์?
. คำตอบนี้ถูกตั้งค่าสถานะโดยบุคคลอื่นซึ่งไม่ได้แสดงความคิดเห็นไว้ข้างหลัง ฉันเพิ่งได้รับสิ่งนี้ในคิวการตรวจสอบ อาจจะเป็นเรื่องเลวร้ายของฉันที่ฉันควรระวังให้มากขึ้น
ฉันคิดว่าวิธีแก้ปัญหาที่ง่ายที่สุดที่ได้รับแรงบันดาลใจจากโซลูชันRay Hulhaคือการทำให้วัตถุเป็นอนุกรมแล้วเปรียบเทียบผลลัพธ์ดิบอย่างลึกซึ้ง
การทำให้เป็นอนุกรมอาจเป็น byte, json, xml หรือ simple toString เป็นต้น ToString ดูเหมือนจะถูกกว่า ลอมบอกสร้าง ToSTring ที่ปรับแต่งได้ง่ายฟรีสำหรับเรา ดูตัวอย่างด้านล่าง
@ToString @Getter @Setter
class foo{
boolean foo1;
String foo2;
public boolean deepCompare(Object other) { //for cohesiveness
return other != null && this.toString().equals(other.toString());
}
}
Apache ให้บางสิ่งแก่คุณแปลงวัตถุทั้งสองเป็นสตริงและเปรียบเทียบสตริง แต่คุณต้อง Override toString ()
obj1.toString().equals(obj2.toString())
แทนที่ toString ()
หากฟิลด์ทั้งหมดเป็นประเภทดั้งเดิม:
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@Override
public String toString() {return
ReflectionToStringBuilder.toString(this);}
หากคุณมีฟิลด์และ / หรือคอลเล็กชันและ / หรือแผนที่ที่ไม่ใช่แบบดั้งเดิม:
// Within class
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
@Override
public String toString() {return
ReflectionToStringBuilder.toString(this,new
MultipleRecursiveToStringStyle());}
// New class extended from Apache ToStringStyle
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.*;
public class MultipleRecursiveToStringStyle extends ToStringStyle {
private static final int INFINITE_DEPTH = -1;
private int maxDepth;
private int depth;
public MultipleRecursiveToStringStyle() {
this(INFINITE_DEPTH);
}
public MultipleRecursiveToStringStyle(int maxDepth) {
setUseShortClassName(true);
setUseIdentityHashCode(false);
this.maxDepth = maxDepth;
}
@Override
protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
}
@Override
protected void appendDetail(StringBuffer buffer, String fieldName,
Collection<?> coll) {
for(Object value: coll){
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
}
}
@Override
protected void appendDetail(StringBuffer buffer, String fieldName, Map<?, ?> map) {
for(Map.Entry<?,?> kvEntry: map.entrySet()){
Object value = kvEntry.getKey();
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
value = kvEntry.getValue();
if (value.getClass().getName().startsWith("java.lang.")
|| (maxDepth != INFINITE_DEPTH && depth >= maxDepth)) {
buffer.append(value);
} else {
depth++;
buffer.append(ReflectionToStringBuilder.toString(value, this));
depth--;
}
}
}}
ตั้งแต่ Java Objects.deepEquals(Object, Object)
7