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.