Java null ตรวจสอบเหตุใดจึงใช้ == แทน .equals ()


125

ใน Java ฉันได้รับแจ้งว่าเมื่อทำการตรวจสอบ null ควรใช้ == แทน .equals () อะไรคือสาเหตุของเรื่องนี้?


12
สิ่งที่ง่ายที่สุดคือลองตรวจสอบค่าว่างด้วยequals()และดู เมื่อคุณลองจะเห็นได้ชัดทันที
Goran Jovic

อย่างไรก็ตามการค้นหาโดย Google ด้วยคีย์เวิร์ด "java null check" (ไม่มีเครื่องหมายอัญประกาศ) ทำให้ฉันเป็นหนึ่งในรายการยอดนิยมของหัวข้อนี้ซึ่งมีข้อมูลเหมือนกับคำตอบที่นี่
Mitch Schwartz

คำตอบ:


179

มันเป็นสองสิ่งที่แตกต่างกันอย่างสิ้นเชิง ==เปรียบเทียบการอ้างอิงอ็อบเจ็กต์หากมีโดยตัวแปร .equals()ตรวจสอบดูว่าวัตถุสองชิ้นเท่ากันตามสัญญาหรือไม่สำหรับความเท่าเทียมกัน เป็นไปได้ทั้งหมดที่อินสแตนซ์ออบเจ็กต์ที่แตกต่างกันสองอินสแตนซ์จะ "เท่ากัน" ตามสัญญา จากนั้นมีรายละเอียดเล็กน้อยที่เนื่องจากequalsเป็นวิธีการหากคุณพยายามเรียกใช้โดยnullอ้างอิงคุณจะได้รับไฟล์NullPointerException.

ตัวอย่างเช่น:

class Foo {
    private int data;

    Foo(int d) {
        this.data = d;
    }

    @Override
    public boolean equals(Object other) {
        if (other == null || other.getClass() != this.getClass()) {
           return false;
        }
        return ((Foo)other).data == this.data;
    }

    /* In a real class, you'd override `hashCode` here as well */
}

Foo f1 = new Foo(5);
Foo f2 = new Foo(5);
System.out.println(f1 == f2);
// outputs false, they're distinct object instances

System.out.println(f1.equals(f2));
// outputs true, they're "equal" according to their definition

Foo f3 = null;
System.out.println(f3 == null);
// outputs true, `f3` doesn't have any object reference assigned to it

System.out.println(f3.equals(null));
// Throws a NullPointerException, you can't dereference `f3`, it doesn't refer to anything

System.out.println(f1.equals(f3));
// Outputs false, since `f1` is a valid instance but `f3` is null,
// so one of the first checks inside the `Foo#equals` method will
// disallow the equality because it sees that `other` == null

คุณหมายถึงpublic int data?
จ่อคิว

@ Xepoch: ไม่ฉันมักจะไม่สร้างฟิลด์สาธารณะ (แม้ว่ามันจะไม่สำคัญสำหรับตัวอย่างนี้ก็ตาม) ทำไม?
TJ Crowder

@TJ Crowder "ทั้งสองสิ่งต่างกันโดยสิ้นเชิง .. " โดยทั่วไปใช่ อย่างไรก็ตามการใช้งานเริ่มต้นของทั้งสองจะเหมือนกันหากความเข้าใจของฉันถูกต้อง ดูที่ซอร์สโค้ด, .equals () โดยทั่วไปจะทำการตรวจสอบ == hg.openjdk.java.net/jdk7/jdk7/jdk/file/tip/src/share/classes/…
Ayush

1
@Ayush - นั่นคือค่าเริ่มต้นในObjectใช่ แม้ว่าจะถูกแทนที่ด้วยคลาส JDK จำนวนมาก แต่ประเด็นไม่ได้เกี่ยวกับการนำไปใช้ แต่เป็นเรื่องของความหมาย (หมายเหตุด้านข้าง: JDK7 ล้าสมัยไปมาก)
TJ Crowder

ใช่แล้วที่สมเหตุสมผลแค่อยากจะชี้แจง
Ayush

38

ถ้าคุณเรียกใช้.equals()บนnullคุณจะได้รับNullPointerException

ดังนั้นจึงขอแนะนำให้ตรวจสอบความว่างเปล่าก่อนที่จะเรียกใช้วิธีการที่เคยใช้

if(str!=null && str.equals("hi")){
 //str contains hi
}  

ดูเพิ่มเติม


34
โดยทั่วไปตัวอย่างของคุณดีกว่าเขียนเป็นif ("hi".equals(str)).
ColinD

3
@ user368186: ประเด็นไม่ได้อยู่ที่ว่าวิธีการเท่ากับมีการตรวจสอบค่าว่างหรือไม่ หากการอ้างอิงวัตถุของคุณเป็นโมฆะการเรียกsomeObject.equals(null)จะเพิ่มขึ้นNullPointerExceptionโดยไม่ต้องป้อนเมธอดเท่ากับ
Dave Costa

2
@ColinD เห็นด้วยแค่สาธิตที่นี่
Jigar Joshi

2
ขอแนะนำให้หลีกเลี่ยงค่าว่างโดยเสียค่าใช้จ่ายเสมอดังนั้นคุณไม่จำเป็นต้องตรวจสอบค่าว่างเลย;)
fwielstra

2
คุณสามารถใช้ Objects.equals(a, b)มันจะไม่เพิ่ม NullPointerException ได้ตลอดเวลา แต่ก็ยังขึ้นอยู่กับวิธีการ "เท่ากัน" ของ "a" และ "b"
Dominik Minc

29

นอกเหนือจากคำตอบที่ยอมรับ ( https://stackoverflow.com/a/4501084/6276704 ):

ตั้งแต่ Java 1.7 หากคุณต้องการเปรียบเทียบสอง Objects ซึ่งอาจเป็นโมฆะฉันขอแนะนำฟังก์ชันนี้:

Objects.equals(onePossibleNull, twoPossibleNull)

java.util.Objects

คลาสนี้ประกอบด้วยวิธียูทิลิตี้แบบคงที่สำหรับการดำเนินการกับอ็อบเจ็กต์ ยูทิลิตี้เหล่านี้รวมถึงเมธอด null-safe หรือ null-tolerant สำหรับการคำนวณรหัสแฮชของอ็อบเจ็กต์การส่งคืนสตริงสำหรับอ็อบเจ็กต์และเปรียบเทียบอ็อบเจ็กต์สองชิ้น

ตั้งแต่: 1.7


2
เพียงเพื่อให้ผู้อื่นมองเห็นได้ชัดเจนขึ้น (ดูคำตอบของ chin90 หรือJavaDoc ): Objects.equals(null, null)จะกลับมาtrue- จำไว้ว่า
Thomas

20

ใน Java 0 หรือ null เป็นประเภทธรรมดาไม่ใช่อ็อบเจ็กต์

วิธีการเท่ากับ () ไม่ได้ถูกสร้างขึ้นสำหรับประเภทที่เรียบง่าย ประเภทอย่างง่ายสามารถจับคู่กับ ==


4
โหวตให้คำตอบที่เป็นประโยชน์มากที่สุดเมื่อเทียบกับคำตอบที่ชัดเจน "NullPointerException จะถูกส่งกลับ" คำตอบของ herp derp
volk


3

ถ้าตัวแปร Object เป็นโมฆะไม่มีใครสามารถเรียกใช้เมธอด equals () ได้ดังนั้นการตรวจสอบการอ้างอิงอ็อบเจ็กต์ของ null จึงเหมาะสม


2

หากคุณลองเรียกเท่ากับในการอ้างอิงวัตถุว่างคุณจะได้รับข้อยกเว้นของตัวชี้ค่าว่าง


2

ตามแหล่งที่มาไม่สำคัญว่าจะใช้อะไรสำหรับการใช้งานวิธีการเริ่มต้น:

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

แต่คุณไม่แน่ใจเกี่ยวกับequalsคลาสที่กำหนดเอง


เป็นเรื่องสำคัญเนื่องจากequalsสามารถคืนค่าfalseหรือทำให้เกิดNullPointerException(หรือบางอย่างที่แตกต่างออกไปหากequalsวิธีการลบล้างเป็นเรื่องไร้สาระ)
ทอม

2

ถ้าเราใช้วิธี => .equals

if(obj.equals(null))  

// Which mean null.equals(null) when obj will be null.

เมื่อ obj ของคุณเป็นโมฆะมันจะโยน Null Point Exception

ดังนั้นเราควรใช้ ==

if(obj == null)

มันจะเปรียบเทียบการอ้างอิง


2

Object.equals เป็น null ที่ปลอดภัยอย่างไรก็ตามโปรดทราบว่าหากวัตถุสองชิ้นเป็นโมฆะ object.equals จะคืนค่าจริงดังนั้นโปรดตรวจสอบว่าวัตถุที่คุณกำลังเปรียบเทียบไม่ใช่ null (หรือเก็บค่า null) ก่อนที่จะใช้ object การเปรียบเทียบ

String firstname = null;
String lastname = null;

if(Objects.equals(firstname, lastname)){
    System.out.println("equal!");
} else {
    System.out.println("not equal!");
}

ตัวอย่างข้อมูลด้านบนจะคืนค่าเท่ากัน!


ตามที่ระบุไว้ในJavaDoc (ควรอ่านสิ่งเหล่านี้เสมอ) Consequently, if both arguments are null, true is returned. ...:)
Thomas

1

เนื่องจากเท่ากับเป็นฟังก์ชันที่ได้มาจากคลาส Object ฟังก์ชันนี้จึงเปรียบเทียบรายการของคลาส ถ้าคุณใช้กับ null มันจะส่งคืนเนื้อหาสาเหตุที่เป็นเท็จสาเหตุที่เป็นเท็จไม่เป็นโมฆะ นอกจากนี้ == เปรียบเทียบการอ้างอิงกับวัตถุ


ผลลัพธ์จะเป็นได้falseหรือNullPointerException(ถ้าequalsไม่ถูกลบล้างสิ่งที่ไม่ดี)
ทอม

1

นี่คือตัวอย่างที่str != nullแต่str.equals(null)เมื่อใช้org.json

 JSONObject jsonObj = new JSONObject("{field :null}");
 Object field = jsonObj.get("field");
 System.out.println(field != null);        // => true
 System.out.println( field.equals(null)); //=> true
 System.out.println( field.getClass());  // => org.json.JSONObject$Null




แก้ไข: นี่คือคลาส org.json.JSONObject $ Null :

/**
 * JSONObject.NULL is equivalent to the value that JavaScript calls null,
 * whilst Java's null is equivalent to the value that JavaScript calls
 * undefined.
 */
private static final class Null {

    /**
     * A Null object is equal to the null value and to itself.
     *
     * @param object
     *            An object to test for nullness.
     * @return true if the object parameter is the JSONObject.NULL object or
     *         null.
     */
    @Override
    public boolean equals(Object object) {
        return object == null || object == this;
    }  
}

ปัญหาที่นี่คือfield.equals(null)ผลตอบแทนจริง สิ่งนี้จะทำลายพฤติกรรม Java ตามปกติและทำให้เกิดความสับสน มันควรจะใช้ได้ผลfield.equals("null")อย่างน้อยก็ในมุมมองของฉัน ฉันไม่รู้ว่าทำไมนักพัฒนาห้องสมุดถึงคิดว่าสิ่งนี้น่าจะสนับสนุน
ทอม

Btw ประโยคแรกของคุณมีปัญหาด้านไวยากรณ์และไม่ชัดเจนว่าคุณหมายถึงอะไร คุณหมายถึง "นี่คือตัวอย่างที่str != nullและ str.equals(null)ผลตอบแทนtrueเมื่อใช้org.json " ใช่ไหม
ทอม

ฉันคิดว่านั่นเป็นเพราะjsonObjectคีย์มี "ฟิลด์" จึงfieldไม่เป็นโมฆะมันมีการอ้างอิงที่มีjson.org.JSONObject$Null วัตถุ
dina

ใช่ แต่ฉันจะไม่รักษาNullเหมือนnullและจะใช้"null"แทน แต่ฉันเดาว่าพวกเขาทำเช่นนั้นเพื่อหลีกเลี่ยงการต้องใช้สตริง แต่แม้จะมี lib ที่field.equals(null)ยังคงเกือบเสมอปัญหา: P
ทอม

0

ดังนั้นฉันจึงไม่สับสนและหลีกเลี่ยงปัญหาเกี่ยวกับวิธีแก้ปัญหานี้:

if(str.trim().length() <=0 ) {
   // is null !
}

5
ถ้า str เป็นโมฆะนี่จะเป็น NPE
typoerrpr

นอกจากนี้สตริงว่าง ( ""มีความยาว 0) เป็นสิ่งที่แตกต่างไปจากการnullอ้างอิงโดยสิ้นเชิง(กล่าวคือไม่มีสตริง)
Thomas

0

ฉันเคยเจอกรณีนี้เมื่อคืนนี้
ฉันพิจารณาแล้วว่า:

ไม่มีเมธอด equals ()สำหรับnull
ดังนั้นคุณไม่สามารถเรียกใช้เมธอดที่ไม่มีอยู่ได้หากคุณไม่มี
- >>> นั่นคือเหตุผลว่าทำไมเราถึงใช้==เพื่อตรวจสอบnull


0

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

   obj = Optional.ofNullable(object1)
    .map(o -> o.getIdObject11())
    .map(o -> o.getIdObject111())
    .map(o -> o.getDescription())
    .orElse("")

ด้านบนคือการตรวจสอบลำดับชั้นของวัตถุดังนั้นให้ใช้

Optional.ofNullable(object1) 

หากคุณมีวัตถุที่จะตรวจสอบเพียงชิ้นเดียว

หวังว่านี่จะช่วยได้ !!!!


-3

คุณสามารถทำได้เสมอ

if (str == null || str.equals(null))

ขั้นแรกจะตรวจสอบการอ้างอิงอ็อบเจ็กต์จากนั้นตรวจสอบอ็อบเจ็กต์ที่ระบุว่าการอ้างอิงไม่เป็นโมฆะ


if (str == null || str.equals (null) || str.equals (""))
Lou Morda

ฉันใช้คำตอบของคุณและเพิ่มการตรวจสอบสตริงว่าง! ถ้าฉันไม่เข้าใจผิดโมฆะและ "" ไม่ใช่สิ่งเดียวกัน
Lou Morda

4
การเพิ่มการตรวจสอบค่าว่างครั้งที่ 2 นั้นไม่ซ้ำซ้อนหรือไม่
Justin Rowe

2
@JustinRowe มันไม่ได้เป็นเพียงซ้ำซ้อนก็ยังเป็นอย่างมากที่ไม่ถูกต้อง x.equals(null)โปรดไม่เคยทำสิ่งที่ชอบ
ทอม

@ ทอม JustinRowe โปรดดูคำตอบของฉันด้านบนว่าทำไมสิ่งนี้ไม่ซ้ำซ้อนหรือไม่สมบูรณ์ขยะ stackoverflow.com/questions/4501061/…
dina
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.