วิธีเขียนเท่ากับ () ใน Java

Feb 22 2023
ในตอนท้ายของบทความนี้ ข้อมูลที่จำเป็นทั้งหมดจะครอบคลุมเพื่อให้สามารถเขียนวิธีการเท่ากับ () ที่ดีที่สุดในภาษาการเขียนโปรแกรม Java รายละเอียดของความเท่าเทียมกันจะได้รับการจัดการในแง่ของคำจำกัดความทางคณิตศาสตร์และในลักษณะของภาษาการเขียนโปรแกรม Java เพื่อให้ครอบคลุมข้อมูลพื้นฐานที่จำเป็นในการใช้แนวทางปฏิบัติที่ดีที่สุดของวิธีการเท่ากับ ()

ในตอนท้ายของบทความนี้ ข้อมูลที่จำเป็นทั้งหมดจะครอบคลุมเพื่อให้สามารถเขียนวิธีการเท่ากับ () ที่ดีที่สุดในภาษาการเขียนโปรแกรม Java รายละเอียดของความเท่าเทียมกันจะได้รับการจัดการในแง่ของคำจำกัดความทางคณิตศาสตร์และในลักษณะของภาษาการเขียนโปรแกรม Java เพื่อให้ครอบคลุมข้อมูลพื้นฐานที่จำเป็นในการใช้แนวทางปฏิบัติที่ดีที่สุดของวิธีการเท่ากับ( )

นิยามทางคณิตศาสตร์ของความเท่าเทียมกัน

ตามWikipediaคำจำกัดความของความเท่าเทียมกันทางคณิตศาสตร์คือ

ในวิชาคณิตศาสตร์ ความเท่าเทียมกันคือความสัมพันธ์ระหว่างปริมาณสองปริมาณหรือโดยทั่วไปมากกว่าสองนิพจน์ทางคณิตศาสตร์ โดยยืนยันว่าปริมาณมีค่าเท่ากัน หรือนิพจน์เป็นตัวแทนของวัตถุทางคณิตศาสตร์เดียวกัน ความเท่าเทียมกันระหว่างAและBจะเขียนว่าA = B และออกเสียง ว่า Aเท่ากับB

นิยามของความเท่าเทียมกันในภาษาจาวา

ในภาษาโปรแกรม Java ความเท่าเทียมกันหมายถึงการมีค่าเท่ากัน บางครั้งมีความหมายตามตัวอักษรว่ามีค่าเท่ากันและบางครั้งก็หมายถึงมีโครงสร้างเหมือนกันในแง่ของคุณสมบัติของวัตถุ

เมื่อจำเป็นต้องตรวจสอบความเท่าเทียมกันของค่าประเภทดั้งเดิมสองค่า เครื่องหมายเท่ากับจะถูกใช้โดยตรงตามตัวอย่างด้านล่าง

int a = 5;
int b = 6;
int c = 5;

// Prints "false" since a and b do not have the same value.
System.out.println(a == b);

// Prints "true" since a and c have the same value.
System.out.println(a == c);

ในทางกลับกัน บางครั้งจำเป็นต้องตรวจสอบความเท่าเทียมกันของวัตถุสองชิ้น ในแง่ของวัตถุ มีสองแนวทางที่แตกต่างกันซึ่งใช้ในขั้นตอนต่างๆ ของกระบวนการตรวจสอบความเท่าเทียมกันของวัตถุสองชิ้น มาดูรายละเอียดกัน

ความเท่าเทียมกันในการอ้างอิง

ในภาษาการเขียนโปรแกรม Java ค่าจริงของอ็อบเจกต์จะถูกเก็บไว้ที่ไหนสักแห่งในหน่วยความจำ และค่าเหล่านั้นจะถูกชี้โดยการอ้างอิงอ็อบเจกต์ซึ่งถูกเก็บไว้ที่ไหนสักแห่งในหน่วยความจำด้วย ลองดูตัวอย่างโค้ดต่อไปนี้

// Create an object with "new" operator.
// Point to object with object reference "a".
Object a = new Object();

// Create another object reference.
// Point to the same object as the reference "a" is pointing.
Object b = a;

// Output is true.
System.out.println(a == b);

// Create another object with "new" operator.
// Point to object with object reference "b".
b = new Object();

// Output is false.
System.out.println(a == b);

ในสถานการณ์ดังกล่าว "a" และ "b" เป็นข้อมูลอ้างอิงสองรายการที่ชี้ไปยังวัตถุเดียวกัน ดังนั้นจึงสามารถพูดได้โดยตรงว่า "a" เท่ากับ "b" เนื่องจากได้ค่าความเท่าเทียมกันของการอ้างอิง

อย่างไรก็ตาม สถานการณ์ตรงข้ามไม่ได้ถูกต้องตลอดเวลา การอ้างอิงที่แตกต่างกันสองรายการอาจชี้ตำแหน่งหน่วยความจำต่างกัน แต่วัตถุที่อ้างอิงอาจเท่ากัน

ในชีวิตปกติ บางครั้งก็เปรียบเทียบปริมาณ เช่น ถ้าคุณมีแอปเปิ้ลสองผลและเพื่อนของคุณมีแอปเปิ้ลสองผล จำนวนแอปเปิ้ลที่คุณและเพื่อนมีเท่ากัน

ในทางกลับกัน บางครั้งอาจมีการเปรียบเทียบสิ่งต่าง ๆ ในแง่ของคุณสมบัติหรือโครงสร้างของมันด้วย เรามาดูรายละเอียดสำหรับสถานการณ์นี้กัน

ความเท่าเทียมกันของโครงสร้าง

ความเท่าเทียมกันของโครงสร้างคือประเภทที่เปรียบเทียบวัตถุตามสถานะของคุณสมบัติ จำเป็นต้องใช้เครื่องมือที่ซับซ้อนกว่านี้เพื่อตรวจสอบความเท่าเทียมกันประเภทนี้ แทนที่จะใช้ตัวดำเนินการ "==" เท่านั้น มันเป็นวิธีการเท่ากับ () มันมาจากคลาส Object ซึ่งอยู่ที่จุดสูงสุดของลำดับชั้นของคลาสในภาษาโปรแกรม Java ตรวจสอบข้อมูลโค้ด Java ที่กำลังจะมาถึง

public class Car {

    // Assume that we have corresponding constructor, accessors, mutators, etc.

    private String brand;
    private String model;
    private Integer modelYear;
    private Integer mileage;
    
}

Car car1 = new Car("Audi","R8", 2023);
Car car2 = new Car("Audi","R8", 2023);
Car car3 = new Car("Audi","R8", 2021);

System.out.println(car1.equals(car2));
System.out.println(car2.equals(car3));

public boolean equals(Object obj) {
    return (this == obj);
}

public class Car {

    private String brand;
    private String model;
    private Integer modelYear;
    private Integer mileage;

    // Assume that we have corresponding constructor, accessors, mutators, etc.

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o; 
        return brand.equals(car.brand) && model.equals(car.model) && modelYear.equals(car.modelYear);
    }
    
}

ในบรรทัดที่สอง เราเริ่มตรวจสอบตัวตนของวัตถุ หากการอ้างอิงวัตถุoเป็นโมฆะหรือการอ้างอิงวัตถุทั้งสองชี้ไปที่วัตถุที่อยู่ในคลาสที่แตกต่างกัน อาจกล่าวได้โดยตรงว่าวัตถุทั้งสองนี้ไม่มีตัวตนเดียวกันและไม่เท่ากัน หากไม่เป็นเช่นนั้น เราก็สามารถตรวจสอบโครงสร้างที่แท้จริงของวัตถุได้ในที่สุด

ในบรรทัดที่สามของเมธอด เราส่งการอ้างอิงoไปยังประเภทคลาสที่เกี่ยวข้อง เนื่องจากเมธอดถูกแทนที่จากคลาสออบเจกต์ และตอนนี้เรารู้แล้วว่ามันอยู่ในคลาสคาร์

สุดท้าย ในบรรทัดสุดท้ายของวิธีการ โครงสร้างของวัตถุสองชิ้นจะถูกตรวจสอบ และนี่คือบรรทัดจริงที่กำหนดตัวตน พูดง่ายๆ ถ้ารุ่นของแบรนด์และรุ่นปีเหมือนกัน วัตถุทั้งสองนี้ก็มีค่าเท่ากัน นี่คือความเท่าเทียมกันทางโครงสร้าง

เราได้เรียนรู้คุณสมบัติของการเท่ากันทางคณิตศาสตร์ที่โรงเรียนประถมศึกษา คุณสมบัติบางอย่างเหล่านี้ต้องใช้ได้กับ เมธอด เท่ากับ ()ด้วย หมายความว่าต้องเพิ่มคุณสมบัติเหล่านี้ลงในส่วนผสมของวิธีการเท่ากับ () มาดูคุณสมบัติเหล่านี้ในแง่ของความเท่าเทียมกันทางคณิตศาสตร์และในแง่ของความเท่าเทียมกันใน Java

คุณสมบัติสะท้อนแสง

ในนิยามทางคณิตศาสตร์ของสมบัติสะท้อน สำหรับจำนวนจริงทั้งหมดx ;

x = x

จำนวนเท่ากับตัวมันเอง

ในแง่ของความเท่าเทียมกันของ Java วัตถุจะต้องเท่ากับตัวเอง

Car car1 = new Car("Audi","R8",2013);
System.out.println(car1.equals(car1));

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o; 
        return mileage < car.mileage && brand.equals(car.brand) && model.equals(car.model) && modelYear.equals(car.modelYear);
    }

คุณสมบัติสมมาตร

ในแง่ของนิยามทางคณิตศาสตร์ของสมบัติสมมาตร สำหรับจำนวนจริงทั้งหมดxและy ;

ถ้าx = yแล้วy = x

ลำดับความเท่าเทียมกันไม่สำคัญ

ในแง่ของความเท่าเทียมกันของ Java ถ้าวัตถุ a เท่ากับวัตถุ b ดังนั้นวัตถุ b จะต้องเท่ากับวัตถุ a;

Car car1 = new Car("Audi","R8",2013);
Car car2 = new Car("Audi","R8",2013);
System.out.println(car1.equals(car2));
System.out.println(car2.equals(car1));

public class Car {

    private String brand;
    private String model;
    private Integer modelYear;
    private Integer mileage;

    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof Car)) return false;
        Car car = (Car) o;
        return brand.equals(car.brand) && model.equals(car.model) && modelYear.equals(car.modelYear);
    }
}

public class SportCar extends Car {
    
    private String engineType;

    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof SportCar)) return false;
        SportCar sportCar = (SportCar) o;
        return engineType.equals(sportCar.engineType);
    }
}

Car car1 = new Car("Audi", "R8", 2012, 1000);
SportCar car2 = new SportCar("Audi", "R8", 2012, 1000, "4.0");
System.out.println(car1.equals(car2));
System.out.println(car2.equals(car1));

คุณสมบัติสกรรมกริยา

ในนิยามทางคณิตศาสตร์ของคุณสมบัติสกรรมกริยา สำหรับจำนวนจริงทั้งหมดx , yและz ;

ถ้าx = yและy = zแล้วx = z

จำนวนสองจำนวนที่เท่ากับจำนวนเดียวกันมีค่าเท่ากัน

ในแง่ของความเท่าเทียมกันของ Java ถ้าวัตถุ a เท่ากับวัตถุ b และวัตถุ b เท่ากับวัตถุ c ดังนั้นวัตถุ a จะต้องเท่ากับวัตถุ c;

SportCar car1 = new SportCar("Audi", "R8", 2012, 1000, new Turbo("100RPM"));
Car car2 = new Car("Audi", "R8", 2012, 1000);
SportCar car3 = new SportCar("Audi", "R8", 2012, 1000, new Turbo("101RPM"));
System.out.println(car1.equals(car2));
System.out.println(car2.equals(car3));
System.out.println(car1.equals(car3));

public class SportCar extends Car {
    
    private Turbo turbo;

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Car)) return false;
        if (!(o instanceof SportCar)) return o.equals(this);
        return super.equals(o) && ((SportCar) o).turbo == turbo;
    }
}

public class Turbo {
    private String power;

    public Turbo(String power) {
        this.power = power;
    }
}

SportCar car1 = new SportCar("Audi", "R8", 2012, 1000, new Turbo("100RPM"));
Car car2 = new Car("Audi", "R8", 2012, 1000);
SportCar car3 = new SportCar("Audi", "R8", 2012, 1000, new Turbo("101RPM"));
System.out.println(car1.equals(car2));
System.out.println(car2.equals(car3));
System.out.println(car1.equals(car3));

คุณสมบัติที่สอดคล้องกัน

นี่เป็นคุณสมบัติที่แตกต่างกันเนื่องจากไม่มีค่าเทียบเท่าที่แน่นอนทางคณิตศาสตร์ สำหรับสิ่งนี้ เราจำเป็นต้องได้รับผลลัพธ์เดียวกันสำหรับการดำเนินการที่ต่อเนื่องกันของวิธีการเท่ากับ ()

ลองดูข้อมูลโค้ดด้านล่าง

Car car1 = new Car("Audi", "R8", 2012, 1000);
Car car2 = new Car("Audi", "R8", 2012, 1000);

for (int i = 0; i < 100000; i++) {
    System.out.println(car1.equals(car2));
}

@Override
public boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof Car)) return false;
    Car car = (Car) o;
    Random random = new Random();
    return brand.equals(car.brand) && model.equals(car.model) && (random.nextInt(10) + 2013) == car.modelYear;
}

หมายเหตุ:ตัวอย่างนี้ไม่ได้ทำให้ฉันพอใจจริงๆ หลังจากการค้นคว้าอย่างถี่ถ้วน ฉันจะแทนที่ด้วยอันที่ดีกว่า มีการกล่าวไว้อย่างชัดเจนในเอกสาร Java ของวิธีการเท่ากับ () ว่า"การเรียกใช้หลายครั้งของx.equals(y)การส่งคืนที่สม่ำเสมอtrueหรือการส่งคืนที่สม่ำเสมอfalse" ต้องมีตัวอย่างที่ชัดเจนของการละเมิดสำหรับข้อความนี้

คุณสมบัติพารามิเตอร์ Null

นี่คือวิธีที่ฉันเลือกเรียกคุณสมบัตินี้

โปรดดูข้อมูลโค้ดด้านล่าง

Car car1 = new Car("Audi", "R8", 2012, 1000);
Car car2 = null;
System.out.println(car1.equals(car2));

ตอนนี้เป็นที่ชัดเจนว่าคุณสมบัติใดที่ควรใช้เมธอดเท่ากับ () เพื่อให้การคำนวณความเท่าเทียมกันถูกต้อง นอกจากนี้ เราได้เห็นสถานการณ์การละเมิดที่เป็นไปได้ ดังนั้นเราจึงสามารถหลีกเลี่ยงสิ่งเหล่านี้ได้เพื่อประโยชน์ของคุณภาพของโค้ดและตรรกะทางธุรกิจ

ปัจจุบัน IDEs จำนวนมากมีกลไกการสร้างเพื่อสร้าง การใช้งานวิธีการเท่ากับ ()ที่ดีที่สุดเท่าที่จะเป็นไปได้ เป็นความคิดที่ดีที่จะอยู่กับพวกเขาเป็นส่วนใหญ่แทนที่จะใช้วิธีเท่ากับ () ลองดูตัวอย่างจากIntellij IDEA ;

@Override
   public boolean equals(Object o) {
       if (this == o) return true;
       if (o == null || getClass() != o.getClass()) return false;
       Car car = (Car) o;
       return brand.equals(car.brand) && model.equals(car.model) && modelYear.equals(car.modelYear) && mileage.equals(car.mileage);
   }

ในบรรทัดแรก มีการตรวจสอบความเท่าเทียมกันของการอ้างอิง เนื่องจากการอ้างอิงวัตถุสองรายการอาจชี้ไปที่วัตถุเดียวกัน

ในบรรทัดที่สอง การตรวจสอบความถูกต้องของNull Parameter Propertyและเช่นเดียวกันกับเงื่อนไข if ที่ทำหน้าที่เป็นguard if clauseโดยการควบคุมประเภทคลาสของออบเจ็กต์ที่ชี้ให้เห็นโดยการอ้างอิง เพื่อป้องกันการละเมิดคุณสมบัติสกรรมกริยาและคุณสมบัติสมมาตร .

ในบรรทัดที่สาม การอ้างอิงออบเจกต์จะถูกส่งไปยังประเภทคลาสที่เกี่ยวข้องซึ่งก็คือ Car ในตัวอย่างของเรา เพื่อเข้าถึงคุณสมบัติของออบเจ็กต์ Car

สุดท้าย ในบรรทัดที่สี่ มีการตรวจสอบความเท่าเทียมกันในแง่ของความเท่าเทียมกันของโครงสร้าง

มีเทมเพลตอื่นสำหรับการสร้างวิธีการเท่ากับ () คุณยังสามารถไปตรวจสอบได้

หากคุณต้องการเขียนการนำไปใช้ด้วยตัวคุณเอง โปรดทราบว่าการนำไปใช้งานต้องรับรองความถูกต้องของคุณสมบัติเมธอดเท่ากับทั้งหมด ()

เมื่อเรามาถึงตอนท้ายของบทความแล้ว ยังมีช่องว่างที่สำคัญมากที่ต้องครอบคลุม เปิด IDE ของคุณและลองสร้างวิธีการเท่ากับ () คุณจะเห็นว่าไม่มีตัวเลือกในการสร้างวิธีเท่ากับ () โดยไม่มี วิธีแฮชโค้ด () เนื่องจากคอลเลกชั่นบางประเภทในภาษาการเขียนโปรแกรม Java จึงเป็นแนวปฏิบัติที่ดีมากที่จะไม่แยกสองวิธีนี้ออกจากกัน มีการกล่าวไว้ในเอกสาร Java อย่างเป็นทางการว่าโดยทั่วไปจำเป็นต้องแทนที่เมธอดhashCode()เมื่อใดก็ตามที่เมธอดเท่ากับ ()ถูกแทนที่ ฉันต้องการให้ข้อมูลที่สอดคล้องกันเกี่ยวกับวิธีการแฮชและแฮชโค้ด ()ในบทความอื่นของฉัน

นอกจากนี้ บางครั้งเราอาจไม่จำเป็นต้องยึดติดกับ คุณสมบัติของเมธอด เท่ากับ ()สำหรับกรณีพิเศษบางอย่าง เช่น คลาสเอนทิตีของ JPA นอกจากนี้ยังสมควรได้รับบทความของตัวเอง

สรุปแล้ว

ประเด็นสำคัญทั้งหมดสำหรับการใช้ วิธี เท่ากับ () ที่ถูก ต้องจะกล่าวถึงในบทความนี้ ฉันยังแนะนำให้คุณเขียนการทดสอบหน่วยที่เกี่ยวข้องเพื่อทดสอบการตรวจสอบคุณสมบัติ ของ เมธอดเท่ากับ () อย่าลืมดูแหล่งข้อมูลอื่น ๆ ที่สามารถพบได้บนเว็บ

ประการแรก ดูเหมือนเป็นหัวข้อที่ง่ายและดั้งเดิมมาก เนื่องจากมาจากพื้นฐานของการออกแบบเชิงวัตถุ อย่างไรก็ตาม ตามที่เห็นจากรายละเอียด สิ่งสำคัญอย่างยิ่งที่จะไม่ข้ามแนวคิดพื้นฐานเหล่านี้และทำความเข้าใจอย่างถ่องแท้