“ สิ่งนี้” สามารถเป็นค่าว่างใน Java ได้หรือไม่


109

เห็นบรรทัดนี้ในวิธีการคลาสและปฏิกิริยาแรกของฉันคือการเยาะเย้ยนักพัฒนาที่เขียนมัน .. แต่แล้วฉันคิดว่าฉันควรแน่ใจว่าฉันคิดถูกก่อน

public void dataViewActivated(DataViewEvent e) {
    if (this != null)
        // Do some work
}

เส้นนั้นจะประเมินเป็นเท็จหรือไม่?


104
มักจะเยาะเย้ยก่อนและตั้งคำถามในภายหลัง การขอโทษมันง่ายกว่าที่จะคว้าโอกาสทองในการทำลายใครบางคนด้วยความวุ่นวาย
Joel Etherton

5
+1 สำหรับคำว่า "flurry of brimstone"
Marty Pitt

13
คุณรู้ว่าอะไรตลก? สิ่งนี้สามารถเกิดขึ้นได้ใน C # เนื่องจากข้อผิดพลาดของคอมไพเลอร์!
Blindy

1
@Blindy จะให้ +1 สำหรับตัวอย่างโค้ด
Nathan Feger

6
ใน C # สามารถเป็นโมฆะได้ ในบางกรณีขอบ ฉันมีแรงกระตุ้นเหมือนกัน: เยาะเย้ยคนดูด แต่แล้วฉันก็สงบลง ดูได้ที่นี่: stackoverflow.com/questions/2464097/…
Andrei Rînea

คำตอบ:


88

ไม่มันไม่ได้ หากคุณกำลังใช้thisว่าคุณอยู่ในอินสแตนซ์จึงthisไม่เป็นโมฆะ

JLS กล่าวว่า:

เมื่อใช้เป็นนิพจน์หลักคีย์เวิร์ดนี้แสดงถึงค่าที่อ้างอิงไปยังอ็อบเจ็กต์ที่เรียกใช้เมธอดอินสแตนซ์ (§15.12) หรือกับอ็อบเจ็กต์ที่กำลังสร้าง

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


แหล่งข้อมูล:


4
ฉันไม่รู้อย่างลึกซึ้งเกี่ยวกับ Java แต่ใน C ++ thisวิธีการอินสแตนซ์อาจเป็น NULL ดังนั้นฉันจึงไม่ค่อยมั่นใจว่านี่เป็นเหตุผลที่เพียงพอใน Java
kennytm

4
ยกเว้นตัวชี้โมฆะในสิ่งที่ต้องการfoo.bar()จะถูกโยนเมื่อมีการค้นพบจะเป็นfoo nullมันเกิดขึ้นก่อนที่จะเข้าสู่เมธอด แต่เรื่องจริงคือไม่มีวิธีใดที่จะพยายามโทร
Claudiu

5
@Kenny ™: เพียงพอใน Java หากคุณใช้thisคีย์เวิร์ดและคอมไพล์จะไม่เป็นโมฆะเมื่อคุณสังเกต แต่อย่างที่คนอื่นบอกว่าไม่ได้ป้องกัน NPE เมื่อพยายามเรียกใช้เมธอดเช่น แต่นั่นอยู่นอกเหนือการควบคุมของคุณเป็นวิธีการและการตรวจสอบค่าว่างภายในเมธอดจะไม่เปลี่ยนแปลงอะไรเลย
Mark Peters

5
@ เคนนี่: ไม่ใช่พฤติกรรมที่ไม่ได้กำหนดแม้ว่าคุณจะทราบรายละเอียดการใช้งานของคุณคุณก็สามารถใช้ได้

4
ฉันจะไม่สมัครรับคำตอบที่ชัดเจนนี้แม้ว่ามันจะสมเหตุสมผลก็ตาม ฉันมีรายงานข้อขัดข้องสำหรับแอป Android โดยที่ค่านี้ == null เมื่อมีการเรียกวิธีอินสแตนซ์ทันทีหลังจากที่ตัวแปรเป็น null-ified จากเธรดอื่น การโทรจริงจะดำเนินต่อไปแม้ว่าตัวแปรจะเป็นโมฆะและจะหยุดทำงานเมื่อพยายามอ่านสมาชิกอินสแตนซ์ :)
Adrian Crețu

63

เหมือนกับถามตัวเองว่า "ฉันยังมีชีวิตอยู่ไหม" thisไม่สามารถเป็นโมฆะได้


44
ฉันฉันมีชีวิตอยู่ ?! โอ้พระเจ้าฉันไม่รู้อีกต่อไป
Claudiu

คุณกำลังทำให้ดูเหมือนthis != nullชัดเจนในตัวเอง ไม่ใช่ - ใน C ++ เช่นthisอาจเป็นNULLวิธีที่ไม่ใช่เสมือน
Niki

1
@nikie ในบางแง่ก็ชัดเจนในตัวเอง แม้ใน C ++ โปรแกรมใด ๆ ที่เกิดเหตุการณ์นี้มีพฤติกรรมที่ไม่ได้กำหนดไว้ นอกจากนี้ยังสามารถเกิดขึ้นได้สำหรับการทำงานเสมือนบน GCC: ideone.com/W7RmU
Johannes Schaub - litb

8
@ จอห์น: คุณคือ ส่วนหนึ่งของการทรมานคือคุณไม่สามารถยืนยันได้

@ JohannesSchaub-litb ไม่เป็นความจริงที่พฤติกรรมของโปรแกรม c ++ ที่มีการอ้างอิงว่างสำหรับสิ่งนี้ไม่ได้กำหนดไว้ กำหนดไว้ตราบเท่าที่คุณไม่หักล้างสิ่งนี้
Rune FS

9

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

และที่น่าสนใจยิ่งกว่านั้นลองตั้งค่า:

this = null;

ลองคิดดู? มันจะเป็นไปได้ยังไงมันจะไม่เหมือนกับการตัดกิ่งไม้ที่คุณกำลังนั่งอยู่ เนื่องจากคีย์เวิร์ด 'this' อยู่ในขอบเขตของคลาสดังนั้นทันทีที่คุณพูด this = null; ที่ใดก็ได้ในชั้นเรียนโดยทั่วไปคุณจะขอให้ JVM ปลดปล่อยหน่วยความจำที่กำหนดให้กับวัตถุนั้นในระหว่างการดำเนินการบางอย่างซึ่ง JVM ไม่สามารถอนุญาตให้เกิดขึ้นได้เนื่องจากจำเป็นต้องกลับมาอย่างปลอดภัยหลังจากเสร็จสิ้นการดำเนินการนั้น

ยิ่งไปกว่านั้นการพยายามthis = null;จะทำให้คอมไพเลอร์ผิดพลาด เหตุผลนั้นค่อนข้างง่ายคีย์เวิร์ดใน Java (หรือภาษาใด ๆ ) ไม่สามารถกำหนดค่าได้เช่นคีย์เวิร์ดไม่สามารถเป็นค่าด้านซ้ายของการดำเนินการกำหนดได้

ตัวอย่างอื่น ๆ คุณไม่สามารถพูดได้ว่า:

true = new Boolean(true);
true = false;

เพื่อนอธิบายได้ดี.
Pankaj Sharma

@PankajSharma Thanks :)
sactiw

ฉันพบสิ่งนี้เพราะฉันต้องการดูว่าฉันสามารถใช้this = null. อินสแตนซ์ของฉันอยู่ใน Android ซึ่งฉันต้องการลบมุมมองและตั้งค่าวัตถุที่จัดการมุมมองเป็น null จากนั้นฉันต้องการใช้เมธอดremove()ที่จะลบมุมมองจริงและตัวจัดการ - อ็อบเจ็กต์จะถูกแสดงผลโดยไร้ประโยชน์ดังนั้นฉันจึงต้องการโมฆะ
Yokich

ไม่แน่ใจว่าฉันเห็นด้วยกับการอ้างสิทธิ์ของคุณ (อย่างน้อยส่วนตรงกลางส่วน lvalue ก็ดี ... แม้ว่าฉันค่อนข้างแน่ใจว่ามีภาษาที่ใช้งานไม่ได้จริงซึ่งอนุญาตให้คุณกำหนดเช่น true = false (และแม้แต่ภาษาที่ฉันจะไม่เรียกว่าหักก็อาจอนุญาตด้วย การสะท้อนกลับหรือกลอุบายที่คล้ายกัน)) อย่างไรก็ตามหากฉันมีวัตถุบางอย่าง foo และฉันต้องทำ (โดยไม่สนใจว่ามันผิดกฎหมาย) foo.this = null foo จะยังคงชี้ไปที่หน่วยความจำดังนั้น JVM จะไม่เก็บขยะ
Foon

@ ไม่นานคำถามก็พูดถึงภาษา Java อย่างแม่นยำยิ่งไปกว่านั้นฉันยังไม่เจอภาษาใด ๆ ที่อนุญาตให้ตั้งค่า this = null นั่นคือถ้ามีภาษาดังกล่าวอยู่ฉันสงสัยอย่างยิ่งว่า 'this' ในภาษาดังกล่าวจะมีบริบทและอุดมการณ์เดียวกัน
sactiw

7

หากคุณรวบรวม-target 1.3หรือก่อนหน้านี้แล้วด้านนอก อาจจะthis nullหรืออย่างน้อยก็เคย ...


2
ฉันเดาว่าผ่านการสะท้อนเราสามารถตั้งค่าด้านนอกเป็นโมฆะได้ อาจเป็นเรื่องตลกของคนโง่ในเดือนเมษายนกับใครบางคนเมื่อเขามีข้อยกเว้นของตัวชี้ที่เป็นโมฆะในการอ้างอิงOuter.this.member
โต้แย้งได้

3

ไม่ใช่ในการเรียกเมธอดของอินสแตนซ์ของคลาสอินสแตนซ์จะต้องมีอยู่ thisตัวอย่างที่ถูกส่งผ่านไปโดยปริยายเป็นพารามิเตอร์กับวิธีการที่อ้างอิงโดย ถ้าthisเป็นเช่นnullนั้นจะไม่มีอินสแตนซ์ที่จะเรียกใช้เมธอด


3

การบังคับใช้ภาษานั้นไม่เพียงพอ VM จำเป็นต้องบังคับใช้ เว้นแต่ VM จะบังคับใช้คุณสามารถเขียนคอมไพเลอร์ที่ไม่บังคับใช้การตรวจสอบค่าว่างก่อนที่จะเรียกใช้เมธอดที่เขียนใน Java opcodes สำหรับวิธีการเช่นการภาวนา ได้แก่ การโหลดโทษเกี่ยวกับเรื่องนี้ไปยังกองเห็น: http://java.sun.com/docs/books/jvms/second_edition/html/Compiling.doc.html#14787 การแทนที่สิ่งนี้สำหรับการอ้างอิงว่างย่อมส่งผลให้การทดสอบเป็นเท็จ


3

ในเมธอดคลาสแบบคงที่thisไม่ได้กำหนดไว้เนื่องจากthisเชื่อมโยงกับอินสแตนซ์ไม่ใช่คลาส ฉันเชื่อว่ามันจะทำให้คอมไพเลอร์มีข้อผิดพลาดในการพยายามใช้thisคีย์เวิร์ดในบริบทคงที่


2

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

ที่มีคุณภาพและthis ควรไม่เป็นnullแต่เป็นไปได้ที่จะทำลายนี้ พิจารณาสิ่งต่อไปนี้:

public class Outer {
   public Outer() {}

   public class Inner {
       public Inner() {}

       public String toString() {
           return "outer is " + Outer.this;  // Qualified this!!
       }
   }
}

เมื่อเราต้องการสร้างอินสแตนซ์Innerเราต้องทำสิ่งนี้:

public static void main(String[] args) {
    Outer outer = new Outer();
    Inner inner = outer.new Inner();
    System.out.println(inner);

    outer = null;
    inner = outer.new Inner();  // FAIL ... throws an NPE
}

ผลลัพธ์คือ:

outer is Outer@2a139a55
Exception in thread "main" java.lang.NullPointerException
        at Outer.main(Outer.java:19)

แสดงว่าความพยายามของเราในการสร้างInnerโดยnullอ้างอิงถึงOuterล้มเหลว

ในความเป็นจริงถ้าคุณติดภายในซอง "Pure Java" คุณจะไม่สามารถทำลายสิ่งนี้ได้

อย่างไรก็ตามแต่ละInnerอินสแตนซ์มีfinalฟิลด์สังเคราะห์ที่ซ่อนอยู่(เรียกว่า"this$0") ซึ่งมีการอ้างอิงถึงไฟล์Outer. หากคุณมีเล่ห์เหลี่ยมจริงๆคุณสามารถใช้วิธี "ไม่บริสุทธิ์" เพื่อกำหนดnullให้กับฟิลด์ได้

  • คุณสามารถใช้ Unsafeเพื่อทำมัน
  • คุณสามารถใช้โค้ดเนทีฟ (เช่น JNI) เพื่อทำได้
  • คุณสามารถทำได้โดยใช้การสะท้อน

ไม่ว่าคุณจะทำด้วยวิธีใดผลลัพธ์สุดท้ายก็คือOuter.thisนิพจน์จะประเมินเป็นnull2 2

ในระยะสั้นจะเป็นไปได้สำหรับการที่มีคุณสมบัติที่จะเป็นthis nullแต่จะเป็นไปไม่ได้หากโปรแกรมของคุณปฏิบัติตามกฎ "Pure Java"


1 - ฉันลดกลเม็ดต่างๆเช่น "เขียน" bytecodes ด้วยมือและส่งต่อเป็น Java จริงปรับแต่ง bytecodes โดยใช้ BCEL หรือคล้ายกันหรือกระโดดลงในโค้ดเนทีฟและดำเนินการกับรีจิสเตอร์ที่บันทึกไว้ IMO นั่นไม่ใช่ Java สมมุติว่าสิ่งเหล่านี้อาจเกิดขึ้นเนื่องจากข้อบกพร่องของ JVM ... แต่ฉันจำไม่ได้ว่าทุกรายงานข้อผิดพลาด

2 - จริงๆแล้ว JLS ไม่ได้บอกว่าพฤติกรรมจะเป็นอย่างไรและอาจขึ้นอยู่กับการนำไปใช้ ... เหนือสิ่งอื่นใด


1

เมื่อคุณเรียกใช้วิธีการnullอ้างอิงNullPointerExceptionจะถูกส่งออกจาก Java VM นี่เป็นไปตามข้อกำหนดดังนั้นหาก Java VM ของคุณปฏิบัติตามข้อกำหนดอย่างเคร่งครัดthisก็จะไม่เป็นnullเช่นนั้น


1

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

C # และ C ++ อนุญาตให้ใช้เมธอดที่ไม่ใช่เสมือน แต่ใน Java เมธอดที่ไม่คงที่ทั้งหมดเป็นเสมือนดังนั้นthisจะไม่เป็นโมฆะ


0

tl; dr, "this" สามารถเรียกได้จากเมธอดที่ไม่คงที่เท่านั้นและเราทุกคนรู้ว่าเมธอดแบบไม่คงที่ถูกเรียกจากอ็อบเจ็กต์บางประเภทซึ่งไม่สามารถเป็นโมฆะได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.