Comment une variable Java peut-elle être différente d'elle-même?

106

Je me demande si cette question peut être résolue en Java (je suis nouveau dans le langage). Voici le code:

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

J'ai reçu la question suivante dans mon laboratoire: Comment pouvez-vous ignorer le premier cas (c'est-à-dire rendre la x == xcondition fausse) sans modifier la condition elle-même?

Husam
la source
12
Je pense qu'il devrait y avoir plus de restrictions, sinon c'est trop ouvert.
Petit étudiant de Fermat
52
Est-ce aussi simple que System.out.println("Gotcha!");le commentaire? :)
stuXnet
7
Bon, alors double a = Double.NaN la réponse la plus courte et mon "hack est juste une triche;)
Christian Kuetbach
47
C'est de jolies anecdotes sur Java, mais j'espère que personne n'envisagera d'en faire une question d'entrevue. Les personnes qui envisagent des candidats à un emploi devraient faire de leur mieux pour déterminer si le candidat comprend la programmation, pas combien de futilités il a accumulées. J'ai à peine utilisé les nombres à virgule flottante en 17 ans de programmation en Java, et encore moins la construction NaN, BEAUCOUP moins savoir comment elle se comporte avec l'opérateur == ...
Arcy
8
@ user1158692 Question d'opinion, je déteste personnellement tous les programmes où les opérateurs de base ont été remplacés et je suis heureux que le code que je reçois en java n'ait pas été modifié (j'ai vu * remplacé comme produit vectoriel croisé et produit scalaire parce que les deux sont des types de multiplication vectorielle; exaspérant! Alors qu'en java, l'un est appelé .cross()et l'autre l'est .dot()et il n'y a pas de confusion. De plus, le fait que "écraser l'opérateur == et toujours retourner faux" ne peut pas arriver semble pro java
Richard Tingle

Réponses:

172

Un moyen simple consiste à utiliser Float.NaN:

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

if (x == x) {
    System.out.println("Ok");
} else {
    System.out.println("Not ok");
}
Pas d'accord

Vous pouvez faire la même chose avec Double.NaN.


À partir de JLS §15.21.1. Opérateurs numériques d'égalité ==et!= :

Les tests d'égalité en virgule flottante sont effectués conformément aux règles de la norme IEEE 754:

  • Si l'un des opérandes est NaN, alors le résultat de ==est falsemais le résultat de !=est true.

    En effet, le test x!=xest truesi et seulement si la valeur de xest NaN.

...

arshajii
la source
157
int x = 0;
if (x == x) {
    System.out.println("Not ok");
} else {
    System.out.println("Ok");
}
Jeroen Vannevel
la source
63
Heh, cela répond totalement à la question posée.
Dave Newton
5
@AswinMurugesh Oui, mais si nous prenons la question au pied de la lettre, comme le fait cette réponse, nous pouvons la supprimer elsecomplètement. Cela ne violerait pas techniquement les termes de la question.
arshajii
67
Considérant que c'est ma deuxième réponse la plus votée, je ne sais pas si je dois conclure que je suis très drôle ou un programmeur de merde.
Jeroen Vannevel
5
@JeroenVannevel Compte tenu des exigences, je pense que c'est la réponse la plus KISS / YAGNI / appropriée;)
Izkata
12
@jddsantaella: évidemment, cela a été modifié par la suite. La question originale disait "Comment puis-je imprimer" pas bien "".
Jeroen Vannevel
147

Par les spécifications du langage Java NaN n'est pas égal à NaN.

Par conséquent, toute ligne qui a causé xla valeur égale à NaNprovoquerait cela, comme

double x=Math.sqrt(-1);

À partir des spécifications du langage Java:

Les opérateurs à virgule flottante ne produisent aucune exception (§11). Une opération qui déborde produit un infini signé, une opération qui déborde produit une valeur dénormalisée ou un zéro signé, et une opération qui n'a pas de résultat mathématiquement défini produit NaN. Toutes les opérations numériques avec NaN comme opérande produisent NaN en conséquence. Comme cela a déjà été décrit, NaN n'est pas ordonné, donc une opération de comparaison numérique impliquant un ou deux NaN renvoie false et toute comparaison! = Impliquant NaN renvoie true, y compris x! = X lorsque x est NaN.

Richard Tingle
la source
@ sᴜʀᴇsʜᴀᴛᴛᴀ Un bon point, j'étais tellement occupé à aller chercher la "convention de codage" que j'ai oublié de répondre à la question
Richard Tingle
Ceci n'est valable que si a est déclaré comme Object ou double.
Christian Kuetbach
1
@ChristianKuetbach Vrai, en l'absence d'informations contraires, j'ai supposé que la ligne commentée pouvait être n'importe quoi
Richard Tingle
2
Même ma réponse trichée est correcte et remplit les règles. Je n'ai édité qu'avant l'instruction if et seul "Gotcha!" Est imprimé. Je suis sûr que cette réponse n'est pas la réponse dans l'esprit du créateur de cette énigme. Mais l'énigme n'est pas bien définie (comme la plupart des projets logiciels ).
Christian Kuetbach
73

Je ne sais pas si c'est une option, mais le passage xd'une variable locale à un champ permettrait à un autre thread de changer sa valeur entre les côtés gauche et droit de lecture de l' ifinstruction.

Voici une courte démo:

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

Production:

⋮
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Not ok
Pshemo
la source
8
Haha +1 pour l'effort, mais mec ... il n'y a aucun moyen que quiconque dans mon laboratoire Java ait trouvé quelque chose comme ça (instructeur inclus).
William Gaul
28
@WilliamGaul Vraiment? J'ai toujours pensé que c'était l'un des exemples de base montrant des problèmes possibles avec le multithreading et pourquoi les gens qui pensent que ce sujet est facile ne devraient jamais être en charge de quoi que ce soit :)
Pshemo
4
Je n'ai même pas pensé à ce problème avant d'avoir lu votre réponse. Merci d'avoir ajouté ceci à ma boîte à outils mentale :)
Behe
56

La ligne remplacée pouvait lire.

double x = Double.NaN;

Cela provoquerait l'impression du gotcha.

La spécification du langage Java (JLS) dit:

Les opérateurs à virgule flottante ne produisent aucune exception (§11). Une opération qui déborde produit un infini signé, une opération qui déborde produit une valeur dénormalisée ou un zéro signé, et une opération qui n'a pas de résultat mathématiquement défini produit NaN. Toutes les opérations numériques avec NaN comme opérande produisent NaN en conséquence. Comme cela a déjà été décrit, NaN n'est pas ordonné, donc une opération de comparaison numérique impliquant un ou deux NaN renvoie false et toute comparaison! = Impliquant NaN renvoie true, y compris x! = X lorsque x est NaN.

Mex
la source
Ou conduire à une erreur de compilation, si a est déclaré comme String a = "Nope"; C'est pourquoi j'ai demandé le type de `` a ''
Christian Kuetbach
Le code ci-dessus ne donne aucune information sur les types, donc j'ai fait l'hypothèse que a n'est pas déjà défini.
Mex
Je pense que votre réponse est la réponse, qui était dans l'esprit du créateur de l'énigme. Mais les règles n'étaient pas claires. Seulement deux règles ont été données: 1. seulement insérer dans la ligne avec le commentaire et 2. obtenir seulement un "Gotcha! Imprimé.
Christian Kuetbach
l'auteur de l'énigme aurait pu rendre toujours valide en entourant le code dans {}
Mex
30

J'ai réussi à obtenir un Gotcha!de ceci:

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;
    }
  }
}
VieuxCurmudgeon
la source
1
Cela change plus que simplement le commentaire que la question demande.
Mex
J'ai essayé quelque chose comme ça mais je pense que c'est garanti que ça produira toujours le même résultat, n'est-ce pas?
AndreDurao
4
@Mex - Oui, mais il préserve suffisamment de l'original pour démontrer un autre point clé que parfois a != aparce qu'il a été modifié par un autre thread. Je soupçonne que cela gagnerait des points dans une interview.
OldCurmudgeon
C'est en fait assez intelligent cependant, je suppose que cela fonctionne en "espérant" qui aest modifié par le premier fil entre le premier et le deuxième accès à des fins de comparaison
Richard Tingle
4
Re vos sceptiques; il est intéressant de noter que tout ce que vous avez écrit au-dessus de la ifdéclaration clé pourrait être écrit en une seule ligne horrible si nécessaire
Richard Tingle
25

Il y a tellement de solutions:

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
la source
1
Sauf si je manque quelque chose, ça super.printlndevrait être "Pas ok", non?
Izkata
@Izkata Oui, je n'ai pas revérifié la sortie souhaitée.
Johannes Kuhn
2
Absolument brillant!
Dariusz
25

Une solution simple est:

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

Mais je ne connais pas toutes les règles de cette énigme ...

:) Je sais que c'est une triche, mais sans connaître toutes les règles, est-ce la solution la plus simple à la question :)

Christian Kuetbach
la source
1
Peut être écrit sur une ligne;)
Christian Kuetbach
6
@ChristianKuetbach Comme tous les programmes
Richard Tingle
2
@ChristianKuetbach En toute honnêteté, le compilateur devrait immédiatement supprimer tous les fichiers de votre ordinateur si vous essayez de programmer comme ça
Richard Tingle
1
Pourquoi rendre cela si difficile? if (System.out.println("Gotcha") && false)
alexis
3
erreur: le type 'void' n'est pas autorisé ici si (System.out.println ("Gotcha") && false) Votre code ne se compilera pas ...
Christian Kuetbach
11

Créez votre propre classe Systemdans le même package avec Condition.
Dans ce cas, votre Systemclasse masquera 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");
        }
    }
}  

DÉMO Ideone

Il y a
la source
9

En utilisant la même approche de sortie de saut / changement d'une autre réponse:

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
la source