Come può una variabile Java essere diversa da se stessa?


106

Mi chiedo se questa domanda possa essere risolta in Java (sono nuovo nel linguaggio). Questo è il codice:

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

Ho ricevuto la seguente domanda nel mio laboratorio: come puoi saltare il primo caso (cioè rendere x == xfalsa la condizione) senza modificare la condizione stessa?


12
Credo che dovrebbero esserci più restrizioni, altrimenti è troppo aperto.
Fermat's Little Student

52
È semplice come System.out.println("Gotcha!");invece del commento? :)
stuXnet

7
Bene, allora è double a = Double.NaN la risposta più breve e il mio "hack è solo un trucco;)
Christian Kuetbach

47
Questa è una simpatica curiosità su Java, ma spero che nessuno prenda in considerazione di farne una domanda per l'intervista. Le persone che considerano i candidati per un impiego dovrebbero fare del loro meglio per capire se il candidato comprende la programmazione, non quante curiosità ha accumulato. Ho usato a malapena numeri in virgola mobile in 17 anni di programmazione in Java, tanto meno il costrutto NaN, MOLTO meno sapendo come si comporta con l'operatore == ...
arcy

8
@ user1158692 Per opinione, personalmente odio tutti i programmi in cui gli operatori di base sono stati sovrascritti e sono contento che qualsiasi codice che ricevo in java non sia stato modificato (ho visto * sovrascritto come prodotto incrociato vettoriale e prodotto punto perché entrambi sono tipi di moltiplicazione vettoriale; esasperante! Mentre in java uno è chiamato .cross()e l'altro è .dot()e non c'è confusione. Anche il fatto che "sovrascrivi l'operatore == e ritorni sempre false" non può accadere sembra pro java
Richard Tingle

Risposte:


172

Un modo semplice è usare Float.NaN:

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

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

Puoi fare lo stesso con Double.NaN.


Da JLS §15.21.1. Operatori di uguaglianza numerica ==e!= :

Il test di uguaglianza in virgola mobile viene eseguito in conformità con le regole dello standard IEEE 754:

  • Se uno degli operandi è NaN, il risultato di ==è falsema il risultato di !=è true.

    In effetti, il test x!=xè truese e solo se il valore di xè NaN.

...


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

63
Eh, questo risponde totalmente alla domanda come chiesto.
Dave Newton

5
@AswinMurugesh Giusto, ma se prendiamo la domanda completamente alla lettera come fa questa risposta, allora possiamo eliminare del elsetutto. Questo tecnicamente non violerebbe i termini della domanda.
arshajii

67
Considerando che questa è la mia seconda risposta più votata, non sono sicuro se concludere che sono molto divertente o un programmatore schifoso.
Jeroen Vannevel

5
@JeroenVannevel Dati i requisiti, penso che questa sia la risposta più KISS / YAGNI / appropriata;)
Izkata

12
@jddsantaella: ovviamente questo è stato modificato in seguito. La domanda originale diceva "Come posso stampare" non ok "".
Jeroen Vannevel

147

Dalle specifiche del linguaggio Java NaN non è uguale a NaN.

Pertanto qualsiasi linea che ha causato xessere uguale a NaNcauserebbe questo, come

double x=Math.sqrt(-1);

Dalle specifiche del linguaggio Java:

Gli operatori in virgola mobile non producono eccezioni (§11). Un'operazione che ha un overflow produce un infinito con segno, un'operazione che un underflow produce un valore denormalizzato o uno zero con segno e un'operazione che non ha un risultato matematicamente definito produce NaN. Tutte le operazioni numeriche con NaN come operando producono NaN come risultato. Come è già stato descritto, NaN non è ordinato, quindi un'operazione di confronto numerico che coinvolge uno o due NaN restituisce false e qualsiasi! = Confronto che coinvolge NaN restituisce true, incluso x! = X quando x è NaN.


@ sᴜʀᴇsʜᴀᴛᴛᴀ Un punto giusto, ero così impegnato a trovare la "convenzione di codifica" detta che mi sono dimenticato di rispondere effettivamente alla domanda
Richard Tingle

Questo è valido solo se a è dichiarato come Object o double.
Christian Kuetbach

1
@ChristianKuetbach Vero, in assenza di informazioni contrarie, ho supposto che la riga commentata possa essere qualsiasi cosa
Richard Tingle

2
Anche la mia risposta imbrogliata è corretta e soddisfa le regole. Ho modificato solo prima dell'istruzione if e solo "Gotcha! È stampato. Sono sicuro che questa risposta non è la risposta nella mente del creatore di questo indovinello. Ma l'enigma non è ben definito (come la maggior parte dei progetti software ).
Christian Kuetbach

73

Non sono sicuro che questa sia un'opzione, ma il passaggio xda una variabile locale a un campo consentirebbe ad un altro thread di modificare il suo valore tra la lettura a sinistra ea destra ifnell'istruzione.

Ecco una breve demo:

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

Produzione:


Ok
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Not ok

8
Haha +1 per lo sforzo, ma amico ... non è possibile che qualcuno nel mio laboratorio Java avrebbe inventato qualcosa del genere (istruttore incluso).
William Gaul

28
@WilliamGaul Davvero? Ho sempre pensato che sia uno degli esempi di base che mostrano possibili problemi con il multithreading e perché le persone che pensano che questo argomento sia facile non dovrebbero mai essere responsabili di nulla :)
Pshemo

4
Non ho nemmeno pensato a questo problema finché non ho letto la tua risposta. Grazie per aver aggiunto questo al mio toolkit mentale :)
Behe

56

La riga sostituita potrebbe leggere.

double x = Double.NaN;

Ciò causerebbe la stampa del gotcha.

Java Language Specification (JLS) dice:

Gli operatori in virgola mobile non producono eccezioni (§11). Un'operazione che eccede produce un infinito con segno, un'operazione che genera un valore insufficiente produce un valore denormalizzato o uno zero con segno e un'operazione che non ha un risultato matematicamente definito produce NaN. Tutte le operazioni numeriche con NaN come operando producono NaN come risultato. Come è già stato descritto, NaN non è ordinato, quindi un'operazione di confronto numerico che coinvolge uno o due NaN restituisce false e qualsiasi! = Confronto che coinvolge NaN restituisce true, incluso x! = X quando x è NaN.


Oppure porta a un errore di compilazione, se a è dichiarato come String a = "Nope"; Ecco perché ho chiesto il tipo di "a"
Christian Kuetbach

Il codice sopra non fornisce informazioni sui tipi, quindi ho assunto che a non sia già definito.
Mex

Penso che la tua risposta sia la risposta, che era nella mente del creatore di indovinelli. Ma le regole non erano chiare. Sono state date solo due regole: 1. inserire solo nella riga con il commento e 2. ottenere solo un "Gotcha! Stampato.
Christian Kuetbach

l'autore dell'indovinello avrebbe potuto rendere sempre valido racchiudendo il codice in {}
Mex

30

Sono riuscito a ottenere un Gotcha!da questo:

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
Questo cambia più del semplice commento come richiesto dalla domanda.
Mex

Ho provato qualcosa del genere ma penso che sia garantito che produrrà sempre lo stesso risultato, non è vero?
AndreDurao

4
@Mex - Sì, ma conserva abbastanza dell'originale per dimostrare un ulteriore punto chiave che a volte a != aè stato modificato da un altro thread. Sospetto che questo farebbe guadagnare punti in un'intervista.
OldCurmudgeon

Questo in realtà è abbastanza intelligente, però, presumo che funzioni "sperando" che asia alterato dal primo thread tra il primo e il secondo accesso per il confronto
Richard Tingle

4
Re i tuoi dubbiosi; vale la pena notare che tutto ciò che hai scritto sopra l' ifaffermazione chiave potrebbe essere scritto in una singola riga orribile, se necessario
Richard Tingle

25

Ci sono tante soluzioni:

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
A meno che non mi manchi qualcosa, super.printlndovrebbe essere "Non ok", giusto?
Izkata

@Izkata Sì, non ho ricontrollato quale dovrebbe essere l'output desiderato.
Johannes Kuhn

2
Assolutamente brillante!
Dariusz

25

Una semplice soluzione è:

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

Ma non conosco tutte le regole di questo indovinello ...

:) So che questo è un trucco, ma senza conoscere tutte le regole, è questa la soluzione più semplice alla domanda :)


1
Può essere scritto in una riga;)
Christian Kuetbach

6
@ChristianKuetbach Come tutti i programmi
Richard Tingle

2
@ChristianKuetbach In tutta franchezza, il compilatore dovrebbe eliminare immediatamente tutti i file sul tuo computer se provassi a programmare effettivamente in quel modo
Richard Tingle

1
Perché renderlo così difficile? if (System.out.println("Gotcha") && false)
alexis

3
errore: il tipo 'void' non è consentito qui se (System.out.println ("Gotcha") && false) Il tuo codice non verrà compilato ...
Christian Kuetbach

11

Crea la tua classe Systemnello stesso pacchetto con Condition.
In questo caso la tua Systemclasse nasconderà la java.lang.Systemclasse

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


9

Utilizzando lo stesso approccio salta / modifica l'output da un'altra risposta:

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");
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.