วิธีการของจาวาที่มีคอมไพล์ประเภทส่งคืนโดยไม่มีคำสั่งส่งคืน


228

คำถามที่ 1:

ทำไมรหัสต่อไปนี้จึงรวบรวมโดยไม่มีคำสั่ง return

public int a() {
    while(true);
}

หมายเหตุ: Unreachable Code Errorหากฉันจะเพิ่มผลตอบแทนหลังจากที่ในขณะที่แล้วผมได้รับ

คำถามที่ 2:

ในทางกลับกันทำไมรหัสต่อไปนี้จึงรวบรวม

public int a() {
    while(0 == 0);
}

แม้ว่าสิ่งต่อไปนี้จะไม่เป็นเช่นนั้น

public int a(int b) {
    while(b == b);
}

2
ไม่ซ้ำซ้อนกับstackoverflow.com/questions/16789832/ …ขอบคุณในช่วงครึ่งหลังของคำถามที่ 2
TJ Crowder

คำตอบ:


274

คำถามที่ 1:

ทำไมรหัสต่อไปนี้จึงรวบรวมโดยไม่มีคำสั่ง return

public int a() 
{
    while(true);
}

สิ่งนี้ครอบคลุมโดยJLS§8.4.7 :

หากมีการประกาศให้มีประเภทผลตอบแทน (§8.4.5) ข้อผิดพลาดในการคอมไพล์เวลาจะเกิดขึ้นหากเนื้อหาของวิธีการนั้นสามารถดำเนินการได้ตามปกติ (§14.1)

กล่าวอีกนัยหนึ่งวิธีการที่มีประเภทผลตอบแทนจะต้องกลับมาโดยใช้คำสั่งส่งคืนที่ให้ผลตอบแทนมูลค่า; วิธีการนี้ไม่ได้รับอนุญาตให้ "เลื่อนออกจากจุดสิ้นสุดของร่างกาย" ดูที่§ 14.17 สำหรับกฎที่แม่นยำเกี่ยวกับข้อความสั่งคืนในเนื้อหาวิธีการ

มันเป็นไปได้สำหรับวิธีการที่จะมีประเภทผลตอบแทนและยังไม่มีคำสั่งกลับมา นี่คือตัวอย่างหนึ่ง:

class DizzyDean {
    int pitch() { throw new RuntimeException("90 mph?!"); }
}

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

คำถามที่ 2:

ในทางกลับกันทำไมรหัสต่อไปนี้จึงรวบรวม

public int a() 
{
    while(0 == 0);
}

แม้ว่าสิ่งต่อไปนี้จะไม่เป็นเช่นนั้น

public int a(int b)
{
    while(b == b);
}

ใน0 == 0กรณีคอมไพเลอร์รู้ว่าการวนซ้ำจะไม่สิ้นสุด (ซึ่ง0 == 0จะเป็นจริงเสมอ) แต่ก็ไม่b == bทราบว่า

ทำไมจะไม่ล่ะ?

คอมไพเลอร์มีความเข้าใจในการแสดงออกคงที่ (§15.28) Quoting §15.2 - รูปแบบของการแสดงออก (เพราะแปลก ๆ ประโยคนี้ไม่อยู่ใน§15.28) :

นิพจน์บางตัวมีค่าที่สามารถกำหนดได้ในเวลารวบรวม นี่คือการแสดงออกที่คงที่ (§15.28)

ในb == bตัวอย่างของคุณเนื่องจากมีตัวแปรที่เกี่ยวข้องจึงไม่ใช่นิพจน์คงที่และไม่ได้ถูกระบุเพื่อกำหนดในเวลารวบรวม เราสามารถเห็นได้ว่ามันจะเป็นจริงเสมอในกรณีนี้ (แม้ว่าจะbเป็น a double, ตามที่ QBrute ชี้ให้เห็น , เราสามารถถูกหลอกได้ง่าย ๆDouble.NaN, ซึ่งไม่ใช่==ตัวของมันเอง ), แต่ JLS ระบุว่าการแสดงออกคงที่นั้น มันไม่อนุญาตให้คอมไพเลอร์พยายามประเมินนิพจน์ที่ไม่คงที่ bayou.io ยกประเด็นที่ดีสำหรับเหตุผลที่ไม่: ถ้าคุณเริ่มลงไปที่ถนนของการพยายามที่จะตรวจสอบการแสดงออกที่เกี่ยวข้องกับตัวแปรที่รวบรวมเวลาที่คุณหยุด? b == bชัดเจน (เอ้อสำหรับผู้ที่ไม่ใช่NaNค่า) แต่เกี่ยวกับa + b == b + aอะไร หรือ(a + b) * 2 == a * 2 + b * 2? การวาดเส้นที่ค่าคงที่เหมาะสม

ดังนั้นเพราะมันไม่ได้ "ตรวจสอบ" การแสดงออกคอมไพเลอร์ไม่ทราบว่าวงจะไม่ยุติจึงคิดว่าวิธีการที่สามารถกลับมาได้ตามปกติ - returnซึ่งก็ไม่ได้รับอนุญาตให้ทำเพราะมันจำเป็นในการใช้งาน returnดังนั้นจึงบ่นเกี่ยวกับการขาดการ


34

มันอาจเป็นเรื่องที่น่าสนใจที่จะนึกถึงวิธีคืนค่าชนิดไม่ใช่สัญญาว่าจะคืนค่าชนิดที่ระบุ แต่เป็นสัญญาที่จะไม่คืนค่าที่ไม่ใช่ประเภทที่ระบุ ดังนั้นหากคุณไม่คืนสิ่งใดคุณไม่ได้ผิดสัญญาและสิ่งต่อไปนี้ถูกกฎหมาย:

  1. วนรอบตลอดไป:

    X foo() {
        for (;;);
    }
  2. วนเวียนอยู่ตลอดไป:

    X foo() {
        return foo();
    }
  3. ทิ้งข้อยกเว้น:

    X foo() {
        throw new Error();
    }

(ฉันคิดว่าการเรียกซ้ำครั้งเดียวสนุกที่จะคิดเกี่ยวกับ: คอมไพเลอร์เชื่อว่าวิธีการจะคืนค่าประเภทX(สิ่งที่เป็น) แต่มันไม่เป็นความจริงเพราะไม่มีรหัสปัจจุบันที่มีความคิดวิธีการสร้างหรือ จัดหาและX.)


8

ดูที่รหัสไบต์หากสิ่งที่ส่งคืนไม่ตรงกับข้อกำหนดคุณจะได้รับข้อผิดพลาดในการคอมไพล์

ตัวอย่าง:

for(;;) จะแสดง bytecodes:

L0
    LINENUMBER 6 L0
    FRAME SAME
    GOTO L0

สังเกตการขาด bytecode ที่ส่งคืนใด ๆ

สิ่งนี้ไม่เคยถูกตีคืนดังนั้นจึงไม่ส่งคืนชนิดที่ไม่ถูกต้อง

สำหรับการเปรียบเทียบวิธีการเช่น:

public String getBar() { 
    return bar; 
}

จะส่งคืนโค้ดไบต์ต่อไปนี้:

public java.lang.String getBar();
    Code:
      0:   aload_0
      1:   getfield        #2; //Field bar:Ljava/lang/String;
      4:   areturn

สังเกต "areturn" ซึ่งหมายถึง "คืนการอ้างอิง"

ตอนนี้ถ้าเราทำต่อไปนี้:

public String getBar() { 
    return 1; 
}

จะส่งคืนโค้ดไบต์ต่อไปนี้:

public String getBar();
  Code:
   0:   iconst_1
   1:   ireturn

ตอนนี้เราสามารถเห็นได้ว่าประเภทในคำจำกัดความไม่ตรงกับประเภทคืนของ ireturn ซึ่งหมายถึง return int

ดังนั้นสิ่งที่เกิดขึ้นจริง ๆ ก็คือหากวิธีการนั้นมีเส้นทางการส่งคืนเส้นทางนั้นจะต้องตรงกับประเภทการคืนสินค้า แต่มีอินสแตนซ์ในไบต์ที่ไม่มีเส้นทางการส่งคืนถูกสร้างขึ้นและทำให้ไม่ผิดกฎ

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