¿Cómo puede una variable de Java ser diferente de sí misma?

106

Me pregunto si esta pregunta se puede resolver en Java (soy nuevo en el idioma). Este es el código:

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

Recibí la siguiente pregunta en mi laboratorio: ¿Cómo se puede omitir el primer caso (es decir, hacer que la x == xcondición sea falsa) sin modificar la condición en sí?

Husam
fuente
12
Creo que debería haber más restricciones, de lo contrario es demasiado abierto.
Little Student de Fermat
52
¿Es tan simple como en System.out.println("Gotcha!");lugar del comentario? :)
stuXnet
7
Bueno, entonces es doble a = Doble.NaN es la respuesta más corta y mi "truco es solo una trampa;)
Christian Kuetbach
47
Esta es una linda trivia de Java, pero espero que nadie considere convertirla en una pregunta de entrevista. Las personas que están considerando candidatos para el empleo deberían hacer lo mejor que puedan para averiguar si el candidato comprende la programación, no cuántas trivialidades ha acumulado. Apenas he usado números de punto flotante en 17 años de programación en Java, mucho menos la construcción NaN, MUCHO menos sabiendo cómo se comporta con el operador == ...
arcy
8
@ user1158692 Cuestión de opinión, personalmente odio cualquier programa en el que se hayan anulado los operadores básicos y me alegro de que ningún código que reciba en Java no haya sido manipulado (he visto * anulado como el producto cruzado vectorial y el producto escalar porque Ambos son tipos de multiplicación de vectores; ¡exasperante! Mientras que en Java se llama a uno .cross()y al otro .dot()y no hay confusión. Además, el hecho de que "anular el operador == y devolver siempre falso" no puede suceder parece pro java
Richard Tingle

Respuestas:

172

Una forma sencilla es utilizar Float.NaN:

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

if (x == x) {
    System.out.println("Ok");
} else {
    System.out.println("Not ok");
}
No está bien

Puedes hacer lo mismo con Double.NaN.


De JLS §15.21.1. Operadores de igualdad numérica ==y!= :

La prueba de igualdad de punto flotante se realiza de acuerdo con las reglas del estándar IEEE 754:

  • Si cualquiera de los operandos es NaN, entonces el resultado de ==es falsepero el resultado de !=es true.

    De hecho, la prueba x!=xes truesi y solo si el valor de xes NaN.

...

arshajii
fuente
157
int x = 0;
if (x == x) {
    System.out.println("Not ok");
} else {
    System.out.println("Ok");
}
Jeroen Vannevel
fuente
63
Je, esto responde totalmente a la pregunta tal como se hizo.
Dave Newton
5
@AswinMurugesh Correcto, pero si tomamos la pregunta completamente literalmente como lo hace esta respuesta, entonces podemos eliminar por elsecompleto. Técnicamente, esto no violaría los términos de la pregunta.
arshajii
67
Teniendo en cuenta que esta es mi segunda respuesta más votada, no estoy seguro de si llegar a la conclusión de que soy muy gracioso o un programador de mierda.
Jeroen Vannevel
5
@JeroenVannevel Dados los requisitos, creo que esta es la respuesta más KISS / YAGNI / apropiada;)
Izkata
12
@jddsantaella: obviamente esto se editó después. La pregunta original decía "¿Cómo puedo imprimir 'no está bien'"?
Jeroen Vannevel
147

Según las especificaciones del lenguaje Java NaN no es igual a NaN.

Por lo tanto, cualquier línea que cause xser igual a NaNcausaría esto, como

double x=Math.sqrt(-1);

De las especificaciones del lenguaje Java:

Los operadores de coma flotante no producen excepciones (§11). Una operación que se desborda produce un infinito con signo, una operación que se desborda produce un valor desnormalizado o un cero con signo, y una operación que no tiene un resultado matemáticamente definido produce NaN. Todas las operaciones numéricas con NaN como operando producen NaN como resultado. Como ya se ha descrito, NaN no está ordenado, por lo que una operación de comparación numérica que involucra uno o dos NaN devuelve falso y cualquier comparación! = Que involucre NaN devuelve verdadero, incluyendo x! = X cuando x es NaN.

Richard Tingle
fuente
@ sᴜʀᴇsʜᴀᴛᴛᴀ Un punto justo, estaba tan ocupado yendo a buscar dicha "convención de codificación" que olvidé realmente responder la pregunta
Richard Tingle
Esto solo es válido si a se declara como Object o double.
Christian Kuetbach
1
@ChristianKuetbach Es cierto, en ausencia de cualquier información que indique lo contrario, asumí que la línea comentada puede ser cualquier cosa
Richard Tingle
2
Incluso mi respuesta engañada es correcta y cumple las reglas. Solo edité antes de la declaración if y solo se imprimió "¡Te tengo! Estoy seguro de que esta respuesta no es la respuesta en la mente del creador de este acertijo. Pero el acertijo no está bien definido ).
Christian Kuetbach
73

No estoy seguro de si esta es una opción, pero cambiar xde variable local a un campo permitiría que otro hilo cambie su valor entre la lectura del lado izquierdo y derecho en la ifdeclaración.

Aquí hay una breve demostración:

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

Salida:

⋮
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Not ok
Pshemo
fuente
8
Jaja +1 por esfuerzo, pero hombre ... no hay forma de que a alguien en mi laboratorio de Java se le haya ocurrido algo como esto (instructor incluido).
William Gaul
28
@WilliamGaul ¿De verdad? Siempre pensé que es uno de los ejemplos básicos que muestra posibles problemas con el subproceso múltiple y por qué las personas que piensan que este tema es fácil nunca deberían estar a cargo de nada :)
Pshemo
4
Ni siquiera pensé en este tema hasta que leí tu respuesta. Gracias por agregar esto a mi kit de herramientas mental :)
Behe
56

La línea reemplazada podía leer.

double x = Double.NaN;

Esto haría que se imprimiera el gotcha.

La especificación del lenguaje Java (JLS) dice:

Los operadores de coma flotante no producen excepciones (§11). Una operación que se desborda produce un infinito con signo, una operación que se desborda produce un valor desnormalizado o un cero con signo, y una operación que no tiene un resultado matemáticamente definido produce NaN. Todas las operaciones numéricas con NaN como operando producen NaN como resultado. Como ya se ha descrito, NaN no está ordenado, por lo que una operación de comparación numérica que involucra uno o dos NaN devuelve falso y cualquier comparación! = Que involucre NaN devuelve verdadero, incluyendo x! = X cuando x es NaN.

Mex
fuente
O conducir a un error de compilación, si a se declara como String a = "Nope"; Por eso pregunté por el tipo de 'a'
Christian Kuetbach
El código anterior no proporciona información sobre los tipos, por lo tanto, asumí que a aún no está definido.
Mex
Creo que tu respuesta es la respuesta, que estaba en la mente del creador de acertijos. Pero las reglas no estaban claras. Solo se dieron dos reglas: 1. Solo inserte en la línea con el comentario y 2. Solo obtenga una impresión de "¡
Te tengo
el autor del acertijo podría haber hecho siempre válido rodeando el código en {}
Mex
30

Me las arreglé para obtener una Gotcha!de esto:

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;
    }
  }
}
ViejoCurmudgeon
fuente
1
Esto cambia más que solo el comentario que pide la pregunta.
Mex
He intentado algo así, pero creo que está garantizado que siempre producirá el mismo resultado, ¿no?
AndreDurao
4
@Mex: de hecho, pero conserva suficiente parte del original para demostrar un punto clave adicional que a veces se a != adebe a que fue cambiado por otro hilo. Sospecho que esto ganaría puntos en una entrevista.
OldCurmudgeon
Sin embargo, esto es bastante inteligente, supongo que funciona "esperando" que ael primer hilo entre el primer y el segundo acceso lo altere para comparar
Richard Tingle
4
Son sus escépticos; Vale la pena señalar que todo lo que ha escrito sobre la ifdeclaración clave podría escribirse en una sola línea horrible si es necesario
Richard Tingle
25

Hay tantas soluciones:

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
fuente
1
A menos que me esté perdiendo algo, super.printlndebería ser "No está bien", ¿verdad?
Izkata
@Izkata Sí, no volví a comprobar cuál debería ser la salida deseada.
Johannes Kuhn
2
¡Absolutamente brillante!
Dariusz
25

Una solución sencilla es:

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

Pero no conozco todas las reglas de este acertijo ...

:) Sé que esto es una trampa, pero sin conocer todas las reglas, ¿es esta la solución más fácil a la pregunta :)

Christian Kuetbach
fuente
1
Se puede escribir en una línea;)
Christian Kuetbach
6
@ChristianKuetbach Como todos los programas
Richard Tingle
2
@ChristianKuetbach Para ser justos, el compilador debería eliminar de inmediato todos los archivos en su computadora si realmente intentó programar así
Richard Tingle
1
¿Por qué hacerlo tan difícil? if (System.out.println("Gotcha") && false)
alexis
3
error: el tipo 'void' no se permite aquí si (System.out.println ("Gotcha") && false) Su código no se compilará ...
Christian Kuetbach
11

Crea tu propia clase Systemen el mismo paquete con Condition.
En este caso, su Systemclase ocultará la java.lang.Systemclase.

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

DEMO de Ideone

Ilya
fuente
9

Usando el mismo enfoque de salida de salto / cambio de otras respuestas:

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
fuente