Bagaimana variabel Java bisa berbeda dari dirinya sendiri?

106

Saya bertanya-tanya apakah pertanyaan ini dapat diselesaikan di Java (saya baru mengenal bahasa ini). Ini kodenya:

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");
        }
    }
}

Saya menerima pertanyaan berikut di lab saya: Bagaimana Anda bisa melewati kasus pertama (yaitu membuat x == xkondisi salah) tanpa mengubah kondisi itu sendiri?

Husam
sumber
12
Saya percaya harus ada lebih banyak batasan, jika tidak maka akan terlalu terbuka.
Siswa Kecil Fermat
52
Apakah sesederhana itu, System.out.println("Gotcha!");bukan komentar? :)
stuXnet
7
Bagus, maka dobel a = Double.NaN jawaban terpendek dan "hack saya hanyalah cheat;)
Christian Kuetbach
47
Ini adalah hal-hal sepele Jawa yang lucu, tapi saya harap tidak ada yang mempertimbangkan menjadikannya pertanyaan wawancara. Orang yang mempertimbangkan kandidat untuk pekerjaan harus melakukan yang terbaik yang mereka bisa untuk mengetahui apakah kandidat memahami pemrograman, bukan berapa banyak hal sepele yang dia kumpulkan. Saya hampir tidak pernah menggunakan angka floating point dalam 17 tahun pemrograman di Java, apalagi konstruksi NaN, JAUH kurang mengetahui bagaimana perilakunya dengan operator == ...
arcy
8
@ user1158692 Soal pendapat, saya pribadi benci program di mana operator dasarnya telah diganti dan saya senang bahwa kode apa pun yang saya terima di java belum diubah (saya telah melihat * diganti sebagai produk silang vektor dan produk titik karena keduanya adalah jenis perkalian vektor; menyebalkan! Sedangkan di java satu dipanggil .cross()dan yang lainnya ada .dot()dan tidak ada kebingungan. Juga fakta bahwa "menimpa operator == dan selalu mengembalikan false" tidak bisa terjadi tampaknya pro java
Richard Tingle

Jawaban:

172

Salah satu cara sederhana adalah dengan menggunakan Float.NaN:

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

if (x == x) {
    System.out.println("Ok");
} else {
    System.out.println("Not ok");
}
Tidak baik

Anda dapat melakukan hal yang sama dengan Double.NaN.


Dari JLS §15.21.1. Operator Kesetaraan Numerik ==dan!= :

Pengujian kesetaraan floating-point dilakukan sesuai dengan aturan standar IEEE 754:

  • Jika salah satu operand adalah NaN, maka hasil dari ==is falsetetapi hasil !=adalah true.

    Memang, pengujiannya x!=xadalah truejika dan hanya jika nilainya xadalah NaN.

...

arshajii
sumber
157
int x = 0;
if (x == x) {
    System.out.println("Not ok");
} else {
    System.out.println("Ok");
}
Jeroen Vannevel
sumber
63
Heh, ini benar-benar menjawab pertanyaan seperti yang ditanyakan.
Dave Newton
5
@AswinMurugesh Benar, tetapi jika kita menjawab pertanyaan secara harfiah seperti jawaban ini, maka kita dapat menghapus elsesemuanya. Ini secara teknis tidak akan melanggar persyaratan pertanyaan.
arshajii
67
Mengingat ini adalah jawaban pilihan tertinggi kedua saya, saya tidak yakin apakah akan menyimpulkan bahwa saya sangat lucu atau programmer yang jelek.
Jeroen Vannevel
5
@JeroenVannevel Mengingat persyaratannya, saya pikir ini adalah jawaban yang paling KISS / YAGNI / sesuai;)
Izkata
12
@jddsantaella: jelas ini diedit setelahnya. Pertanyaan asli mengatakan "Bagaimana saya bisa mencetak 'tidak ok'".
Jeroen Vannevel
147

Dengan Spesifikasi Bahasa Java NaN tidak sama dengan NaN.

Oleh karena itu setiap garis yang menyebabkan xmenjadi sama NaNakan menyebabkan hal ini, seperti

double x=Math.sqrt(-1);

Dari Spesifikasi Bahasa Java:

Operator floating-point tidak menghasilkan pengecualian (§11). Operasi yang meluap menghasilkan tak terhingga bertanda, operasi yang meluap menghasilkan nilai yang dinormalisasi atau nol bertanda, dan operasi yang tidak memiliki hasil pasti secara matematis menghasilkan NaN. Semua operasi numerik dengan NaN sebagai operand menghasilkan NaN sebagai hasilnya. Seperti yang telah dijelaskan, NaN tidak berurutan, sehingga operasi perbandingan numerik yang melibatkan satu atau dua NaN mengembalikan false dan setiap! = Perbandingan yang melibatkan NaN mengembalikan true, termasuk x! = X ketika x adalah NaN.

Richard Tingle
sumber
@ sᴜʀᴇsʜᴀᴛᴛᴀ Suatu poin yang adil, saya begitu sibuk pergi untuk menemukan kata "konvensi pengkodean" sehingga saya lupa untuk menjawab pertanyaannya
Richard Tingle
Ini hanya valid jika a dideklarasikan sebagai Object atau double.
Christian Kuetbach
1
@ChristianKuetbach Benar, jika tidak ada informasi yang bertentangan, saya berasumsi bahwa baris yang dikomentari dapat berupa apa saja
Richard Tingle
2
Bahkan jawaban curang saya benar dan memenuhi aturan. Saya mengedit hanya sebelum pernyataan-if dan hanya "Gotcha! ).
Christian Kuetbach
73

Tidak yakin apakah ini adalah opsi tetapi mengubah xdari variabel lokal ke bidang akan memungkinkan utas lain mengubah nilainya antara pembacaan sisi kiri dan kanan dalam ifpernyataan.

Berikut demo singkatnya:

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++;
    }
}

Keluaran:


Ok
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Not ok
Pshemo
sumber
8
Haha +1 untuk usaha, tapi kawan ... tidak mungkin ada orang di lab Java saya yang akan menemukan sesuatu seperti ini (termasuk instruktur).
William Gaul
28
@WilliamGul Benarkah? Saya selalu berpikir bahwa ini adalah salah satu contoh dasar yang menunjukkan kemungkinan masalah dengan multithreading dan mengapa orang yang berpikir bahwa subjek ini mudah seharusnya tidak pernah bertanggung jawab atas apa pun :)
Pshemo
4
Saya bahkan tidak memikirkan masalah ini sampai saya membaca jawaban Anda. Terima kasih telah menambahkan ini ke perangkat mental saya :)
Behe
56

Baris yang diganti bisa dibaca.

double x = Double.NaN;

Ini akan menyebabkan gotcha dicetak.

Spesifikasi Bahasa Java (JLS) mengatakan:

Operator floating-point tidak menghasilkan pengecualian (§11). Operasi yang meluap menghasilkan tak terhingga bertanda, operasi yang meluap menghasilkan nilai yang dinormalisasi atau nol bertanda, dan operasi yang tidak memiliki hasil pasti secara matematis menghasilkan NaN. Semua operasi numerik dengan NaN sebagai operand menghasilkan NaN sebagai hasilnya. Seperti yang telah dijelaskan, NaN tidak berurutan, jadi operasi perbandingan numerik yang melibatkan satu atau dua NaN mengembalikan false dan setiap! = Perbandingan yang melibatkan NaN mengembalikan true, termasuk x! = X ketika x adalah NaN.

Mex
sumber
Atau menyebabkan kesalahan kompilasi, jika a dideklarasikan sebagai String a = "Nope"; Itu sebabnya saya meminta tipe 'a'
Christian Kuetbach
Kode di atas tidak memberikan informasi tentang tipe, oleh karena itu saya berasumsi bahwa a belum ditentukan.
Mex
Saya pikir jawaban Anda adalah jawabannya, itulah yang ada di benak pembuat teka-teki. Tapi aturannya tidak jelas. Hanya dua aturan yang diberikan: 1. hanya menyisipkan di baris dengan komentar dan 2. hanya mendapatkan satu "Gotcha! Dicetak.
Christian Kuetbach
penulis teka-teki bisa membuat selalu valid dengan mengapit kode di {}
Mex
30

Saya berhasil mendapatkan Gotcha!dari ini:

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;
    }
  }
}
OldCurmudgeon
sumber
1
Ini mengubah lebih dari sekedar komentar seperti yang ditanyakan oleh pertanyaan.
Mex
Saya sudah mencoba sesuatu seperti itu tetapi saya pikir dijamin akan selalu menghasilkan hasil yang sama, bukan?
AndreDurao
4
@Mex - Memang benar tetapi cukup mempertahankan aslinya untuk menunjukkan poin kunci lebih lanjut yang terkadang a != akarena diubah oleh utas lain. Saya menduga ini akan memenangkan poin dalam sebuah wawancara.
OldCurmudgeon
Ini sebenarnya cukup pintar, saya berasumsi ini bekerja dengan "harapan" yang adiubah oleh utas pertama di antara akses pertama dan kedua untuk perbandingan
Richard Tingle
4
Re Anda orang-orang yang ragu; perlu dicatat bahwa semua yang Anda tulis di atas ifpernyataan kunci dapat ditulis dalam satu baris yang mengerikan jika perlu
Richard Tingle
25

Ada banyak solusi:

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");
        }
    }
}
Johannes Kuhn
sumber
1
Kecuali saya melewatkan sesuatu, itu super.printlnseharusnya "Tidak oke", bukan?
Izkata
@Izkata Ya, tidak memeriksa ulang apa seharusnya keluaran yang diinginkan.
Johannes Kuhn
2
Benar-benar brilian!
Dariusz
25

Salah satu solusi mudahnya adalah:

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

Tapi saya tidak tahu semua aturan teka-teki ini ...

:) Saya tahu ini adalah cheat, tetapi tanpa mengetahui semua aturan, apakah ini solusi termudah untuk pertanyaan tersebut :)

Christian Kuetbach
sumber
1
Dapat ditulis dalam satu baris;)
Christian Kuetbach
6
@ChristianKuetbach Seperti yang bisa semua program
Richard Tingle
2
@ChristianKuetbach Dalam semua keadilan kompiler harus segera menghapus semua file di komputer Anda jika Anda mencoba untuk benar-benar program seperti itu
Richard Tingle
1
Mengapa membuatnya begitu sulit? if (System.out.println("Gotcha") && false)
alexis
3
kesalahan: jenis 'void' tidak diizinkan di sini jika (System.out.println ("Gotcha") && false) Kode Anda tidak dapat dikompilasi ...
Christian Kuetbach
11

Buat kelas Anda sendiri Systemdalam paket yang sama dengan Condition.
Dalam hal ini Systemkelas Anda akan menyembunyikan java.lang.Systemkelas

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 DEMO

Ilya
sumber
9

Menggunakan pendekatan lewati / ubah keluaran yang sama dari jawaban lain:

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");
        }
    }
}
higuaro
sumber