Heim  >  Artikel  >  Java  >  So überschreiben Sie die Methode equal in Java korrekt

So überschreiben Sie die Methode equal in Java korrekt

WBOY
WBOYnach vorne
2023-05-09 11:19:261380Durchsuche

1. Was ist die Gleichheitsmethode?

Wir müssen zunächst wissen, dass die Objektklasse die übergeordnete Klasse (Superklasse/Basisklasse) aller Klassen in Java ist, also in Java In Code> erben alle Klassen standardmäßig von Object class. Mit anderen Worten, wir können alle in Object class implementierten Methoden direkt verwenden. Die Methode equals ist eine der vielen Methoden, die von der Object-Klasse implementiert werden. Object类Java中所有类的父类(超类/基类),也就是说,在Java中,所有的类都是默认继承自Object类的,换言之,Object类中所实现的方法我们都可以直接拿来用。而equals方法便是Object类所实现的众多方法之一。

以下截图自Java11 API

So überschreiben Sie die Methode equal in Java korrekt

Object类的所有方法:

So überschreiben Sie die Methode equal in Java korrekt

1.1 equals方法:

  • equals:是Object类中的方法,只能判断引用类型,等下可以带大伙看看jdk源码

  • 默认判断的是地址是否相等(因为引用类型变量底层本质就是来存储对象地址的,有C/C++知识的小伙伴应该很了解 ),在子类中往往会重写该方法,用于判断对象的内容是否相等。比如等下会简单了解到的IntegerString(在IDEA里看源码实现 )

2. 为什么要重写equals方法?

我们有Object类实现的equals方法能用不就行了?为啥还得重写equals方法呢?这就要看看Object类equals方法实现机制了。

So überschreiben Sie die Methode equal in Java korrekt

我们可以清楚地看到,Object类equals方法底层是用 == 来实现的,也就是说它的用法跟我们平常用来比较基本数据类型的 == 用法一致。我们首先来看一下 == 的语法:

  • == 只能用来比较基本数据类型是否相等,也就是单纯的值比较;

  • == 在比较浮点数的时候也可能存在失效的情况,这是因为浮点数的存储机制跟整型家族不一样,浮点数本身就不能表示一个精确的值(具体原因可自行查看IEEE 754规则,这里不再展开)

所以我们在单纯的进行基本数据类型的值比较时可以用 == ,而比较引用数据类型就不能这么做,前面有提到,引用数据类型本质上是来引用/存储对象的地址的,所有你完全可以把它当做C/C++的指针来看待(这里杠一句说Java没有指针的,个人觉得只是叫法不同罢了 )
注: 不要把Java引用跟C++引用搞混了,C++引用其实是指针常量,即int* const,这也是C++的引用只能作为一个变量的别名的原因。

2.1 举个例子吧~

比较两个int时可以直接用 == ,当它们相等时结果为true,而当new了两个属性完全一样的对象时,再用 == 来进行比较就会出现错误,如我们所见,明明我们应该想要得到true的,结果却是false
源码:

So überschreiben Sie die Methode equal in Java korrekt

运行结果:

So überschreiben Sie die Methode equal in Java korrekt

到这里,我们应该大致清楚为啥要在比较对象时重写equals方法了,因为Object类提供给我们的不好使~~

3. 分析equals源码:

在进行重写之前,我们依旧来看看Java API中的定义:
public boolean equals(Object obj)
作用:指示某个其他对象是否“等于”此对象。

equals方法在非null对象引用上实现等价关系:

  • 自反性 :对于任何非空的参考值xx.equals(x)应该返回true

  • 对称性 :对于任何非空引用值xyx.equals(y)应该返回true当且仅当y.equals(x)回报true

  • 传递性 :对于任何非空引用值xyz ,如果x.equals(y)回报truey.equals(z)回报true ,然后x.equals(z)应该返回true

    Die folgenden Screenshots stammen von Java11 API🎜🎜„Java🎜🎜Alle Methoden der Object-Klasse: 🎜🎜„So🎜

    1.1-equals-Methode:

    • 🎜equals: Ja Die Methode in <code>Object class kann nur den Referenztyp bestimmen. Den jdk-Quellcode kann ich Ihnen später zeigen🎜
    • 🎜By Standardmäßig wird bestimmt, ob die Adresse gleich ist (da die zugrunde liegende Essenz von Referenztypvariablen darin besteht, Objektadressen zu speichern, was Freunde mit C/C++-Kenntnissen sehr gut wissen sollten). Diese Methode wird in Unterklassen häufig überschrieben, um zu bestimmen, ob der Inhalt von die Objekte sind gleich. Zum Beispiel werden wir kurz etwas über Integer und String erfahren (siehe die Quellcode-Implementierung in IDEA)🎜
    🎜2. Warum sollten wir umschreiben? ist gleich Methode? 🎜🎜Können wir die von der Object-Klasse implementierte Methode equals verwenden? Warum müssen wir die Methode equals überschreiben? Dies hängt vom Implementierungsmechanismus der equals-Methode der Object-Klasse ab. 🎜🎜So schreiben Sie die Methode equal in Java korrekt um🎜🎜Wir können Verstehen Es ist deutlich zu erkennen, dass die equals-Methode der Objektklasse unten mit == implementiert wird, was bedeutet, dass ihre Verwendung mit der von uns üblichen == übereinstimmt Zum Vergleichen grundlegender Datentypen verwenden. Die Verwendung ist konsistent. Werfen wir zunächst einen Blick auf die Syntax von ==: 🎜
    • 🎜== kann nur zum Vergleich grundlegender Datentypen auf Gleichheit verwendet werden, bei dem es sich um einen einfachen Wertevergleich handelt. 🎜 li>
    • 🎜== Beim Vergleich von Gleitkommazahlen kann es auch zu Fehlern kommen. Dies liegt daran, dass sich der Speichermechanismus von Gleitkommazahlen von dem der Ganzzahlfamilie unterscheidet Wert (der spezifische Grund kann sein: Überprüfen Sie die IEEE 754-Regeln selbst, keine weiteren Details hier)🎜
    🎜Wir können also == verwenden, wenn wir einfach Werte grundlegender Datentypen vergleichen, aber das ist nicht möglich Tun Sie dies, wenn Sie Referenzdatentypen vergleichen, wie oben erwähnt. Es wurde erwähnt, dass der Referenzdatentyp im Wesentlichen zum Referenzieren/Speichern der Adresse des Objekts verwendet wird, sodass Sie ihn als Zeiger von C/C++ behandeln können. code> (hier heißt es, dass Java keine Zeiger hat, ich persönlich denke, es ist nur ein anderer Name)<br>Hinweis: Verwechseln Sie die <code>Java-Referenz nicht mit der C++-Referenz. Die C++-Referenz ist eigentlich eine Zeigerkonstante, also <code> int* const, deshalb kann die Referenz von C++ nur als Alias ​​von verwendet werden eine Variable. 🎜

    2.1 Geben wir ein Beispiel~

    🎜Wenn Sie zwei Ints vergleichen, können Sie == direkt verwenden. Wenn sie gleich sind, ist das Ergebnis true und wenn new Wenn zwei Objekte mit genau den gleichen Attributen erhalten werden, tritt ein Fehler auf, wenn == zum Vergleich verwendet wird. Wie wir sehen können, sollten wir offensichtlich true , das Ergebnis ist <code>false
    Quellcode: 🎜🎜So schreiben Sie die Methode equal in Java korrekt um🎜🎜Laufergebnisse: 🎜🎜So schreiben Sie die Methode equals in Java korrekt um🎜🎜An diesem Punkt sollten wir eine ungefähre Vorstellung davon haben, warum wir wann die Methode equals überschreiben müssen Objekte vergleichen, da der uns zur Verfügung gestellte Objektklasse nicht funktioniert~~🎜🎜3 Analysieren Sie den gleichen Quellcode: 🎜🎜Bevor wir ihn neu schreiben, werfen wir noch einen Blick auf die Definition in Java API:
    public boolean equals(Object obj)
    Funktion: Gibt an, ob ein anderes Objekt diesem Objekt „gleich“ ist. 🎜🎜equals-Methode implementiert Äquivalenz für Nicht-Null-Objektreferenzen: 🎜
    • 🎜Reflexivität: für jeden Nicht-Null-Referenzwert x , x.equals(x) sollte true zurückgeben. 🎜
    • 🎜Symmetrie: Für alle Referenzwerte ungleich Null x und y gilt x.equals(y) sollte genau dann true zurückgeben, wenn y.equals(x) true zurückgibt. 🎜
    • 🎜Transitivität: für alle Referenzwerte ungleich Null x, y und z, wenn x .equals(y) gibt true zurück, y.equals(z) gibt true zurück, dann x.equals ( z)sollte true zurückgeben. 🎜
    • Konsistenz: mehrere Aufrufe von x.equals(y für alle Nicht-Null-Referenzwerte <code>x und y ) gibt immer true oder immer false zurück, wenn die im equals-Vergleich für das Objekt verwendeten Informationen nicht geändert wurden. xy ,多次调用x.equals(y)始终返回true或始终返回false ,前提是未修改对象上的equals比较中使用的信息。

    • 对于任何非空的参考值xx.equals(null)应该返回false

    类Object的equals方法实现了对象上最具区别的可能等价关系; 也就是说,对于任何非空引用值x和y ,当且仅当x和y引用同一对象( x == y具有值true )时,此方法返回true 。

    注意:通常需要在重写此方法时覆盖hashCode方法,以便维护hashCode方法的常规协定,该方法声明相等对象必须具有相等的哈希代码。

    So überschreiben Sie die Methode equal in Java korrekt

    接下来看看String类中重写的equals方法和Integer类中重写的equals方法:

    So überschreiben Sie die Methode equal in Java korrekt

    //String类equals源代码:
    public boolean equals(Object anObject) {
            if (this == anObject) {
                return true;
            }
            if (anObject instanceof String) {
                String anotherString = (String)anObject;
                int n = value.length;
                if (n == anotherString.value.length) {
                    char v1[] = value;
                    char v2[] = anotherString.value;
                    int i = 0;
                    while (n-- != 0) {
                        if (v1[i] != v2[i])
                            return false;
                        i++;
                    }
                    return true;
                }
            }
            return false;
        }

    简单解读一下就是当对比的是同一个对象时,直接返回true,提高效率。当传进来的对象是当前类的实例时,进入进一步的判断,一个for循环依次遍历字符串每一个字符,只要有一个字符不同就返回false

    继续来看看Integer类的equals源代码:

    So überschreiben Sie die Methode equal in Java korrekt

    //Integer类的equals源代码:
     public boolean equals(Object obj) {
            if (obj instanceof Integer) {
                return value == ((Integer)obj).intValue();
            }
            return false;
        }

    Integer类equals源码简单许多,只要传入的对象是当前类的实例,就进行进一步的判断:当它们的值相等时,就返回true,不相等就返回false

    这里还是来实际演示一下⑧,就以Integer类为例:

    So überschreiben Sie die Methode equal in Java korrekt

    很明显,我们知道Integer类重写了equals方法且是引用类型。当直接用 == 来比较引用类型变量时,结果是false,而用equals判断结果为true。这便很好地说明了重写equals方法的必要性。String类大伙自己验证一哈⑧。

    4. 正确重写equals方法:

    (先说结论,getClass()instanceof更安全)

    到这里,我们基本把equals方法的各种源码都分析了一遍,接下来就是我们自己要来实现equals方法了。

    这里提供两个比较常见的equals重写方法:

    • instanceof实现重写equals方法

    • getClass实现重写equals方法

    假设有此场景:

    在已经创建好的长方形类中重写Object类中的equals方法为当长方形的长和宽相等时,返回TRUE,同时重写hashCode方法,重写toString方法为显示长方形的长宽信息。并测试类。

    package com.test10_04;
    
    import java.util.Objects;
    
    class Rectangle {
        private double length;
        private double wide;
    
        public Rectangle() {
            //空实现
        }
        public Rectangle(double length, double wide) {
            setLength(length);
            setWide(wide);
        }
    
        public double getLength() {
            return length;
        }
    
        public void setLength(double length) {
            assert length > 0.0 : "您的输入有误,长方形的长不能小于0";
            this.length = length;
        }
    
        public double getWide() {
            return wide;
        }
    
        public void setWide(double wide) {
            assert wide > 0.0 : "您的输入有误,长方形的宽不能小于0";
            this.wide = wide;
        }
    
        public double area() {
            return this.length * this.wide;
    
        }
        public double circumference() {
            return 2 * (this.wide + this.length);
        }
    
        public boolean equals(Object obj) {
            if (this == obj) { //判断一下如果是同一个对象直接返回true,提高效率
                return true;
            }
            if (obj == null || obj.getClass() != this.getClass()) { //如果传进来的对象为null或者二者为不同类,直接返回false
                return false;
            }
            //也可以以下方法:
    //        if (obj == null || !(obj instanceof Rectangle)) { //如果传进来的对象为null或者二者为不同类,直接返回false
    //            return false;
    //        }
            Rectangle rectangle = (Rectangle) obj; //向下转型
            //比较长宽是否相等,注意:浮点数的比较不能简单地用==,会有精度的误差,用Math.abs或者Double.compare
            return Double.compare(rectangle.length, length) == 0 && Double.compare(rectangle.wide, wide) == 0;
        }
    
        public int hashCode() { //重写equals的同时也要重写hashCode,因为同一对象的hashCode永远相等
            return Objects.hash(length, wide); //调用Objects类,这是Object类的子类
        }
    
        public String toString() {
            return "Rectangle{" + "length=" + length + ", wide=" + wide + &#39;}&#39;;
        }
    }
    
    public class TestDemo {
        public static void main(String[] args) {
    
            Rectangle rectangle1 = new Rectangle(3.0, 2.0);
            Rectangle rectangle2 = new Rectangle(3.0, 2.0);
            
            System.out.println(rectangle1.equals(rectangle2));
            System.out.println("rectangle1哈希码:" + rectangle1.hashCode() +
                    "\nrectangle2哈希码:" + rectangle2.hashCode());
            System.out.println("toString打印信息:" + rectangle1.toString());
        }
    }

    具体实现思路在代码中讲的很清楚了,我们这里重点分析一下getClassinstanceof两种实现方法的优缺点:

    So überschreiben Sie die Methode equal in Java korrekt

    将代码逻辑简化一下:
    我们就重点看这段简单的代码

    //getClass()版本
    public class Student {
    	private String name;
    	
        public void setName(String name) {
            this.name = name;
        }
    	@Override
    	public boolean equals(Object object){
    		if (object == this)
    			return true;
    		// 使用getClass()判断对象是否属于该类
    		if (object == null || object.getClass() != getClass())
    			return false;
    		Student student = (Student)object;
    		return name != null && name.equals(student.name);
    }
    //instanceof版本
    public class Student {
    	private String name;
    	
        public void setName(String name) {
           this.name = name;
       }
    	@Override
    	public boolean equals(Object object){
    		if (object == this)
    			return true;
    		// 通过instanceof来判断对象是否属于类
    		if (object == null || !(object instanceof Student))
    			return false;
    		Student student = (Student)object;
    		return name!=null && name.equals(student.name);
    	}
    }

    事实上两种方案都是有效的,区别就是getClass()限制了对象只能是同一个类,而instanceof却允许对象是同一个类或其子类,这样equals方法就变成了父类与子类也可进行equals操作了,这时候如果子类重定义了equals方法,那么就可能变成父类对象equlas子类对象为true,但是子类对象equlas父类对象就为false了,如下所示:

    class GoodStudent extends Student {
    
        @Override
        public boolean equals(Object object) {
            return false;
        }
    
        public static void main(String[] args) {
            GoodStudent son = new GoodStudent();
            Student father = new Student();
    
            son.setName("test");
            father.setName("test");
    
    		// 当使用instance of时
            System.out.println(son.equals(father)); // 这里为false
            System.out.println(father.equals(son)); // 这里为true
    
    		// 当使用getClass()时
            System.out.println(son.equals(father)); // 这里为false
            System.out.println(father.equals(son)); // 这里为false	
        }
    }

    注意看这里用的是getClass()

    So überschreiben Sie die Methode equal in Java korrekt

    返回值两个都是false,符合我们的预期,(连类都不一样那肯定得为false啊)

    So überschreiben Sie die Methode equal in Java korrekt

    而换成instanceof

    So überschreiben Sie die Methode equal in Java korrektFür jeden Referenzwert ungleich Null x sollte x.equals(null) zurückgeben falsch.

    #🎜🎜#
#🎜🎜#Die Methode equal der Klasse Object implementiert die bestmögliche Äquivalenzbeziehung für Objekte; das heißt, für alle Nicht-Null-Referenzwerte x und y, wann und Diese Methode gibt nur dann true zurück, wenn x und y auf dasselbe Objekt verweisen (x == y hat den Wert true). #🎜🎜##🎜🎜#Hinweis: Beim Überschreiben dieser Methode ist es normalerweise erforderlich, die Methode hashCode zu überschreiben, um den allgemeinen Vertrag der Methode hashCode beizubehalten, der besagt, dass gleiche Objekte gleich sein müssen Hash-Codes. #🎜🎜##🎜🎜#So schreiben Sie die Methode equal in Java korrekt um#🎜🎜##🎜🎜# Schauen Sie sich als Nächstes die Methode equals an, die in der Klasse String und der Klasse Integer überschrieben wurde code>equalsMethode: #🎜🎜##🎜🎜#Java So schreiben Sie die Methode equal in #🎜🎜#rrreee#🎜🎜#Eine einfache Erklärung ist, dass true direkt zurückgegeben wird, wenn es sich um dasselbe Objekt handelt, um die Effizienz zu verbessern. Wenn das übergebene Objekt eine Instanz der aktuellen Klasse ist, wird eine weitere Beurteilung eingegeben. Eine for-Schleife durchläuft nacheinander jedes Zeichen der Zeichenfolge, solange ein Zeichen unterschiedlich ist /code> wird zurückgegeben. #🎜🎜##🎜🎜# Schauen wir uns weiterhin den equals-Quellcode der Integer-Klasse an: #🎜🎜##🎜🎜#So überschreiben Sie die Equals-Methode in Java korrekt#🎜🎜#rrreee#🎜🎜# Ganzzahlige Klasse Der Quellcode von <code>equals ist viel einfacher. Solange das eingehende Objekt eine Instanz der aktuellen Klasse ist, wird eine weitere Beurteilung vorgenommen: wann ihre Werte sind gleich, true wird zurückgegeben. Wenn nicht gleich, wird false zurückgegeben. #🎜🎜##🎜🎜# Lassen Sie uns eine praktische Demonstration geben⑧, am Beispiel der Integer-Klasse: #🎜🎜##🎜🎜#So überschreiben Sie die Methode equal in Java korrekt#🎜🎜##🎜🎜#Offensichtlich wissen wir, dass die Klasse Integer Überschreibt die Methode equals und ist ein Referenztyp. Wenn == direkt zum Vergleichen von Referenztypvariablen verwendet wird, ist das Ergebnis false, und wenn equals zur Bestimmung verwendet wird, ist das Ergebnis true. Dies veranschaulicht gut die Notwendigkeit, die Methode equals zu überschreiben. String-Klasse Bitte überprüfen Sie es selbst⑧. #🎜🎜#

4. Schreiben Sie die equal-Methode korrekt um:

#🎜🎜# (Lassen Sie mich zunächst über die Schlussfolgerung sprechen: getClass() ist sicherer als instanceof) #🎜🎜##🎜🎜#An dieser Stelle haben wir grundsätzlich die verschiedenen Quellcodes der <code>equals-Methode analysiert. Der nächste Schritt besteht darin, den equalszu implementieren > Methode selbst. #🎜🎜##🎜🎜#Hier sind zwei gängige equals-Umschreibungsmethoden: #🎜🎜#
    #🎜🎜##🎜🎜 #Verwenden Sie instanceof zum Implementieren des Umschreibens der Methode equals #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Verwenden Sie getClass, um das Umschreiben von zu implementieren ist gleichMethode#🎜🎜##🎜🎜#
#🎜🎜#Nehmen Sie dieses Szenario an: #🎜🎜##🎜🎜#Schreiben Sie die bereits erstellte Rechteckklasse umDer <code>ist gleich in der Klasse >Object gibt TRUE zurück, wenn Länge und Breite des Rechtecks ​​gleich sind, und überschreibt gleichzeitig die Methode hashCode. Überschreiben Sie die Methode toString, um die Längen- und Breiteninformationen des Rechtecks ​​anzuzeigen. und testen Sie die Klasse. #🎜🎜#rrreee#🎜🎜#Die spezifischen Implementierungsideen sind im Code klar dargelegt. Hier konzentrieren wir uns auf die Analyse der Vor- und Nachteile der beiden Implementierungsmethoden getClass und instanceof :#🎜🎜##🎜🎜#So schreiben Sie die Methode equal in korrekt um Java#🎜🎜##🎜🎜#Vereinfachen Sie die Codelogik:
Konzentrieren wir uns auf diesen einfachen Code#🎜🎜#rrreeerrreee#🎜🎜#Tatsächlich sind beide Lösungen gültig, der Unterschied ist getClass() beschränkt das Objekt darauf, derselben Klasse anzugehören, aber instanceof lässt zu, dass das Objekt derselben Klasse oder seiner Unterklasse angehört, sodass die Methode „equals“ zur übergeordneten Klasse wird Wenn die Unterklasse zu diesem Zeitpunkt die Methode „equals“ neu definiert, kann das Unterklassenobjekt „equlas“ wahr sein, das Unterklassenobjekt „equlas“ jedoch falsch, wie folgt: #🎜🎜# rrreee#🎜🎜# Beachten Sie, dass hier getClass() verwendet wird #🎜🎜##🎜🎜#So schreiben Sie die Methode equal in Java korrekt um#🎜🎜##🎜🎜#Beide Rückgabewerte sind false, was unseren Erwartungen entspricht , (auch wenn die Klassen unterschiedlich sind, muss es false sein) #🎜🎜##🎜🎜#So schreiben Sie die Methode equal in Java richtig um#🎜🎜##🎜🎜# Versuchen wir es stattdessen mit instanceof: #🎜🎜## 🎜🎜##🎜🎜##🎜🎜#

Ausführungsergebnisse: Eines ist true und das andere ist false. Es ist offensichtlich, dass ein Problem vorliegt. true一个为false,很明显出现问题了。

So überschreiben Sie die Methode equal in Java korrekt

这里的原因如下:
instanceof的语法是这样的:
当一个对象为一个类的实例时,结果才为true。但它还有一个特点就是,如果当这个对象时其子类的实例时,结果也会为true。这便导致了上述的bug。也就是说当比较的两个对象,他们的类是父子关系时,instanceof可能会出现问题。**需要深究的小伙伴可以自己去了解一哈,所以在这里建议在实现重写equals方法时,尽量使用getClass来实现。

在重写equals方法的同时需要重写hashCode

So schreiben Sie die Methode equal in Java korrekt um🎜🎜Der Grund hier Wie folgt: Die Syntax von
instanceof lautet wie folgt:
Wenn ein Objekt eine Instanz einer Klasse ist, ist das Ergebnis true. Es verfügt aber auch über die Funktion, dass das Ergebnis ebenfalls true ist, wenn dieses Objekt eine Instanz seiner Unterklasse ist. Dies führt zu dem oben genannten Fehler. Das heißt, wenn die beiden verglichenen Objekte eine Eltern-Kind-Beziehung haben, kann instanceof Probleme verursachen. **Freunde, die mehr lernen müssen, können es selbst lernen. Daher wird empfohlen, beim Umschreiben der Methode equals zu versuchen, sie mit getClass zu implementieren. 🎜🎜Wenn Sie die Methode equals überschreiben, müssen Sie die Methode hashCode überschreiben. 🎜

Das obige ist der detaillierte Inhalt vonSo überschreiben Sie die Methode equal in Java korrekt. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen