ตัวแปร Java แตกต่างจากตัวมันเองได้อย่างไร?


106

ฉันสงสัยว่าคำถามนี้สามารถแก้ไขได้ใน Java หรือไม่ (ฉันยังใหม่กับภาษา) นี่คือรหัส:

class Condition {
    // you can change in the main
    public static void main(String[] args) { 
        int x = 0;
        if (x == x) {
            System.out.println("Ok");
        } else {
            System.out.println("Not ok");
        }
    }
}

ฉันได้รับคำถามต่อไปนี้ในห้องปฏิบัติการของฉัน: คุณจะข้ามกรณีแรก (เช่นทำให้x == xเงื่อนไขเป็นเท็จ) โดยไม่แก้ไขเงื่อนไขได้อย่างไร


12
ฉันเชื่อว่าควรมีข้อ จำกัด มากกว่านี้มิฉะนั้นจะเปิดกว้างเกินไป
Fermat's Little Student

52
มันง่ายเหมือนSystem.out.println("Gotcha!");แทนความคิดเห็นหรือไม่? :)
stuXnet

7
ดีแล้วเป็นสองเท่า a = Double.NaN คำตอบที่สั้นที่สุดและ "แฮ็คของฉันก็แค่โกง;)
Christian Kuetbach

47
นี่เป็นเรื่องไม่สำคัญของ Java แต่ฉันหวังว่าจะไม่มีใครพิจารณาให้คำถามสัมภาษณ์ ผู้ที่พิจารณาผู้สมัครเพื่อจ้างงานควรพยายามอย่างเต็มที่เพื่อดูว่าผู้สมัครเข้าใจการเขียนโปรแกรมหรือไม่ไม่ใช่เรื่องเล็กน้อยที่เขาสะสมมา ฉันแทบไม่ได้ใช้ตัวเลขทศนิยมเลยใน 17 ปีของการเขียนโปรแกรมใน Java, โครงสร้าง NaN น้อยลงมาก, ไม่ค่อยรู้ว่ามันทำงานอย่างไรกับตัวดำเนินการ == ...
arcy

8
@ user1158692 เรื่องความคิดเห็นโดยส่วนตัวฉันเกลียดโปรแกรมใด ๆ ที่ตัวดำเนินการพื้นฐานถูกแทนที่และดีใจที่รหัสใด ๆ ที่ฉันได้รับใน java นั้นไม่ได้รับความเสียหาย (ฉันเห็น * แทนที่เป็นผลิตภัณฑ์ข้ามเวกเตอร์และผลิตภัณฑ์ดอทเพราะ ทั้งคู่เป็นชนิดของการคูณเวกเตอร์โกรธมาก! ในขณะที่หนึ่งเรียกว่า java .cross()และอีกอันคือ.dot()และไม่มีความสับสนนอกจากนี้ข้อเท็จจริงที่ว่า "แทนที่ตัวดำเนินการ == และคืนค่าเท็จเสมอ" ไม่สามารถเกิดขึ้นได้ดูเหมือนว่า pro java
Richard Tingle

คำตอบ:


172

วิธีง่ายๆวิธีหนึ่งคือการใช้Float.NaN:

float x = Float.NaN;  // <--

if (x == x) {
    System.out.println("Ok");
} else {
    System.out.println("Not ok");
}
ไม่โอเค

คุณสามารถทำเช่นเดียวกันกับDouble.NaN.


จากJLS §15.21.1 ตัวดำเนินการความเท่าเทียมกันเชิงตัวเลข==และ!= :

การทดสอบความเท่าเทียมกันของจุดลอยตัวดำเนินการตามกฎของมาตรฐาน IEEE 754:

  • หากทั้งสองตัวถูกดำเนินการที่จังหวัดน่านแล้วผลมาจากการ==เป็นfalseแต่ผลของการมี!=true

    อันที่จริงการทดสอบx!=xคือtrueถ้าค่าxเป็น NaN เท่านั้น

...


157
int x = 0;
if (x == x) {
    System.out.println("Not ok");
} else {
    System.out.println("Ok");
}

63
เดี๋ยวก่อนนี่ตอบคำถามทั้งหมดตามที่ถาม
Dave Newton

5
@AswinMurugesh ถูกต้อง แต่ถ้าเราตอบคำถามอย่างสมบูรณ์ตามคำตอบนี้เราก็สามารถลบelseทั้งหมดได้ สิ่งนี้จะไม่ละเมิดข้อกำหนดของคำถามในทางเทคนิค
arshajii

67
เมื่อพิจารณาว่านี่เป็นคำตอบที่ได้รับการโหวตสูงสุดเป็นอันดับสองของฉันฉันไม่แน่ใจว่าจะสรุปได้ว่าฉันตลกมากหรือเป็นโปรแกรมเมอร์ที่เส็งเคร็ง
Jeroen Vannevel

5
@JeroenVannevel จากข้อกำหนดฉันคิดว่านี่เป็นคำตอบที่เหมาะสมที่สุด KISS / YAGNI /;)
Izkata

12
@jddsantaella: เห็นได้ชัดว่ามีการแก้ไขในภายหลัง คำถามเดิมกล่าวว่า "ฉันจะพิมพ์คำว่า" ไม่ตกลง "ได้อย่างไร
Jeroen Vannevel

147

ตามข้อกำหนดของภาษา Java NaNไม่เท่ากับNaN .

ดังนั้นเส้นใดก็ตามที่ทำให้xเท่ากับNaNจะทำให้เกิดสิ่งนี้เช่น

double x=Math.sqrt(-1);

จากข้อกำหนดภาษา Java:

ตัวดำเนินการ Floating-point ไม่มีข้อยกเว้น (§11) การดำเนินการที่ล้นทำให้เกิดอินฟินิตี้ที่มีการลงนามการดำเนินการที่มากเกินไปทำให้เกิดค่าที่ผิดปกติหรือศูนย์ที่มีการลงนามและการดำเนินการที่ไม่มีผลลัพธ์ที่แน่นอนทางคณิตศาสตร์จะสร้าง NaN การดำเนินการตัวเลขทั้งหมดที่มี NaN เป็นตัวถูกดำเนินการจะทำให้เกิด NaN ตามที่ได้อธิบายไปแล้ว NaN ไม่ได้เรียงลำดับดังนั้นการดำเนินการเปรียบเทียบตัวเลขที่เกี่ยวข้องกับ NaN หนึ่งหรือสองรายการจะส่งคืนค่าเท็จและใด ๆ ! = การเปรียบเทียบที่เกี่ยวข้องกับ NaN จะส่งกลับค่าจริงรวมถึง x! = x เมื่อ x เป็น NaN


@ sᴜʀᴇsʜᴀᴛᴛᴀเป็นประเด็นที่ยุติธรรมฉันยุ่งมากกับการออกไปพบว่า "การประชุมการเขียนโค้ด" ซึ่งฉันลืมที่จะตอบคำถามจริงๆ
Richard Tingle

จะใช้ได้ก็ต่อเมื่อ a ถูกประกาศเป็น Object หรือ double
Christian Kuetbach

1
@ChristianKuetbach True ในกรณีที่ไม่มีข้อมูลใด ๆ ในทางตรงกันข้ามฉันคิดว่าบรรทัดที่แสดงความคิดเห็นอาจเป็นอะไรก็ได้
Richard Tingle

2
แม้แต่คำตอบที่โกงของฉันก็ถูกต้องและเป็นไปตามกฎ ฉันแก้ไขเฉพาะก่อนคำสั่ง if และมีเพียง "Gotcha! เท่านั้นที่พิมพ์ออกมาฉันแน่ใจว่าคำตอบนี้ไม่ใช่คำตอบในใจของผู้สร้างปริศนานี้ แต่ปริศนาไม่ได้กำหนดไว้อย่างชัดเจน (เนื่องจากโครงการซอฟต์แวร์ส่วนใหญ่ ).
Christian Kuetbach

73

ไม่แน่ใจว่านี่เป็นตัวเลือกหรือไม่ แต่การเปลี่ยนxจากตัวแปรภายในเป็นฟิลด์จะอนุญาตให้เธรดอื่นเปลี่ยนค่าระหว่างการอ่านด้านซ้ายและด้านขวาในifคำสั่ง

นี่คือการสาธิตสั้น ๆ :

class Test {

    static int x = 0;

    public static void main(String[] args) throws Exception {

        Thread t = new Thread(new Change());
        t.setDaemon(true);
        t.start();

        while (true) {
            if (x == x) {
                System.out.println("Ok");
            } else {
                System.out.println("Not ok");
                break;
            }
        }
    }
}

class Change implements Runnable {
    public void run() {
        while (true)
            Test.x++;
    }
}

เอาท์พุต:

⋮
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Not ok

8
ฮาฮา +1 สำหรับความพยายาม แต่ผู้ชาย ... ไม่มีทางที่ทุกคนในห้องทดลอง Java ของฉันจะคิดแบบนี้ได้ (รวมผู้สอน)
William Gaul

28
@WilliamGaul จริงเหรอ? ฉันมักจะคิดว่านี่เป็นหนึ่งในตัวอย่างพื้นฐานที่แสดงปัญหาที่อาจเกิดขึ้นกับมัลติเธรดและทำไมคนที่คิดว่าเรื่องนี้ง่ายไม่ควรรับผิดชอบอะไรเลย :)
Pshemo

4
ฉันไม่ได้คิดเกี่ยวกับปัญหานี้จนกว่าจะได้อ่านคำตอบของคุณ ขอบคุณสำหรับการเพิ่มเครื่องมือนี้เพื่อจิตของฉัน :)
Behe

56

บรรทัดที่ถูกแทนที่สามารถอ่านได้

double x = Double.NaN;

สิ่งนี้จะทำให้ gotcha ถูกพิมพ์ออกมา

ข้อกำหนดภาษา Java (JLS) กล่าวว่า:

ตัวดำเนินการ Floating-point ไม่มีข้อยกเว้น (§11) การดำเนินการที่ล้นทำให้เกิดอินฟินิตี้ที่มีการลงนามการดำเนินการที่มากเกินไปทำให้เกิดค่าที่ผิดปกติหรือศูนย์ที่มีการลงนามและการดำเนินการที่ไม่มีผลลัพธ์ที่แน่นอนทางคณิตศาสตร์จะสร้าง NaN การดำเนินการตัวเลขทั้งหมดที่มี NaN เป็นตัวถูกดำเนินการจะทำให้เกิด NaN ตามที่ได้อธิบายไปแล้ว NaN ไม่ได้เรียงลำดับดังนั้นการดำเนินการเปรียบเทียบตัวเลขที่เกี่ยวข้องกับ NaN หนึ่งหรือสองรายการจะส่งกลับค่าเท็จและใด ๆ ! = การเปรียบเทียบที่เกี่ยวข้องกับ NaN จะส่งกลับค่าจริงรวมถึง x! = x เมื่อ x เป็น NaN


หรือนำไปสู่ข้อผิดพลาดในการคอมไพล์ถ้า a ถูกประกาศเป็น String a = "Nope"; นั่นเป็นเหตุผลที่ฉันขอประเภทของ 'a'
Christian Kuetbach

โค้ดด้านบนไม่ได้ให้ข้อมูลเกี่ยวกับประเภทดังนั้นฉันจึงตั้งสมมติฐานว่า a ไม่ได้กำหนดไว้แล้ว
Mex

ฉันคิดว่าคำตอบของคุณคือคำตอบซึ่งอยู่ในความคิดของผู้สร้างปริศนา แต่กฎก็ไม่ชัดเจน มีกฎเพียงสองข้อเท่านั้น: 1. ใส่เฉพาะในบรรทัดที่มีความคิดเห็นและ 2. รับเพียงหนึ่ง "Gotcha! พิมพ์ออกมา
Christian Kuetbach

ผู้เขียนปริศนาสามารถทำให้ถูกต้องเสมอโดยล้อมรอบรหัสใน {}
Mex

30

ฉันจัดการเพื่อรับGotcha!จากสิ่งนี้:

volatile Object a = new Object();

class Flipper implements Runnable {
  Object b = new Object();

  public void run() {
    while (true)  {
      Object olda = a;
      a = b;
      a = olda;
    }
  }

}

public void test() {
  new Thread(new Flipper()).start();

  boolean gotcha = false;
  while (!gotcha) {
    // I've added everything above this - I would therefore say still legal.
    if (a == a) {
      System.out.println("Not yet...");
    } else {
      System.out.println("Gotcha!");
      // Uncomment this line when testing or you'll never terminate.
      //gotcha = true;
    }
  }
}

1
สิ่งนี้เปลี่ยนแปลงมากกว่าแค่ความคิดเห็นตามที่คำถามถาม
Mex

ฉันเคยลองอะไรแบบนั้นมาแล้ว แต่ฉันคิดว่ามันรับประกันได้ว่ามันจะได้ผลลัพธ์เหมือนเดิมใช่ไหม?
AndreDurao

4
@Mex - มันทำได้จริง แต่มันรักษาต้นฉบับไว้มากพอที่จะแสดงให้เห็นถึงประเด็นสำคัญเพิ่มเติมที่บางครั้งเป็นa != aเพราะเธรดอื่นเปลี่ยนไป ฉันสงสัยว่านี่จะชนะคะแนนในการสัมภาษณ์
OldCurmudgeon

จริงๆแล้วมันค่อนข้างฉลาด แต่ฉันคิดว่ามันทำงานโดย "หวัง" ที่aเปลี่ยนแปลงโดยเธรดแรกระหว่างการเข้าถึงครั้งแรกและครั้งที่สองเพื่อเปรียบเทียบ
Richard Tingle

4
ตอบข้อสงสัยของคุณ เป็นที่น่าสังเกตว่าทุกสิ่งที่คุณเขียนไว้เหนือifคำสั่งสำคัญสามารถเขียนเป็นบรรทัดเดียวที่น่ากลัวได้หากจำเป็น
Richard Tingle

25

มีวิธีแก้ปัญหามากมาย:

class A extends PrintStream {
    public A(PrintStream x) {super(x);}
    public void println(String x) {super.println("Not ok");}
    public static void main(String[] args) {
        System.setOut(new A(System.out));
        int x = 0;
        if (x == x) {
            System.out.println("Ok");
        } else {
            System.out.println("Not ok");
        }
    }
}

1
ถ้าฉันไม่ได้ทำอะไรบางอย่างนั่นsuper.printlnควรจะ "ไม่เป็นไร" ใช่ไหม?
Izkata

@Izkata ใช่ไม่ได้ตรวจสอบอีกครั้งว่าผลลัพธ์ที่ต้องการควรเป็นอย่างไร
Johannes Kuhn

2
แจ่มแน่นอน!
Dariusz

25

วิธีง่ายๆอย่างหนึ่งคือ:

System.out.println("Gotcha!");if(false)
if( a == a ){
  System.out.println("Not yet...");
} else {
  System.out.println("Gotcha!");
}

แต่ฉันไม่รู้กฎทั้งหมดของปริศนานี้ ...

:) ฉันรู้ว่านี่เป็นการโกง แต่ไม่รู้กฎทั้งหมดนี่เป็นทางออกที่ง่ายที่สุดสำหรับคำถาม :)


1
เขียนได้ในบรรทัดเดียว;)
Christian Kuetbach

6
@ChristianKuetbach เช่นเดียวกับโปรแกรมทั้งหมด
Richard Tingle

2
@ChristianKuetbach ในความเป็นธรรมคอมไพเลอร์ควรลบไฟล์ทั้งหมดในคอมพิวเตอร์ของคุณทันทีหากคุณพยายามเขียนโปรแกรมเช่นนั้นจริง
Richard Tingle

1
ทำไมทำยากจัง if (System.out.println("Gotcha") && false)
alexis

3
ข้อผิดพลาด: ไม่อนุญาตให้พิมพ์ 'void' ที่นี่ถ้า (System.out.println ("Gotcha") && false) โค้ดของคุณจะไม่คอมไพล์ ...
Christian Kuetbach

11

สร้างชั้นเรียนของคุณเองในแพคเกจเดียวกับท่าSystem ในกรณีนี้ชั้นเรียนของคุณจะซ่อนชั้นเรียนCondition
Systemjava.lang.System

class Condition
{
    static class System
    {
        static class out
        {
            static void println(String ignored)
            {
                java.lang.System.out.println("Not ok");
            }
        }
    }

    public static void main (String[] args) throws java.lang.Exception
    {
        int x = 0;
        if (x == x) 
        {
           System.out.println("Not ok");
        } 
        else 
        {
           System.out.println("Ok");
        }
    }
}  

Ideone สาธิต


9

ใช้แนวทางผลลัพธ์การข้าม / เปลี่ยนเดียวกันจากคำตอบอื่น:

class Condition {
    public static void main(String[] args) {
        try {
            int x = 1 / 0;
            if (x == x) {
                System.out.println("Ok");
            } else {
                System.out.println("Not ok");
            }
        } catch (Exception e) {
            System.out.println("Not ok");
        }
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.