Como uma variável Java pode ser diferente de si mesma?

106

Estou me perguntando se essa questão pode ser resolvida em Java (eu sou novo na linguagem). Este é o 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");
        }
    }
}

Recebi a seguinte pergunta em meu laboratório: Como você pode ignorar o primeiro caso (ou seja, tornar a x == xcondição falsa) sem modificar a própria condição?

Husam
fonte
12
Acredito que deveria haver mais restrição, senão é muito aberto.
Fermat's Little Student,
52
É tão simples como em System.out.println("Gotcha!");vez do comentário? :)
stuXnet
7
Bom, então é duplo a = Duplo.NaN a resposta mais curta e meu "hack é apenas um cheat;)
Christian Kuetbach
47
Essas são curiosidades interessantes sobre Java, mas espero que ninguém pense em fazer disso uma pergunta para uma entrevista. Pessoas que estão considerando candidatos para emprego devem fazer o melhor que podem para descobrir se o candidato entende de programação, não quantas curiosidades ele acumulou. Eu quase não usei números de ponto flutuante em 17 anos de programação em Java, muito menos a construção NaN, MUITO menos sabendo como se comporta com o operador == ...
arcy
8
@ user1158692 Questão de opinião, eu pessoalmente odeio qualquer programa em que os operadores básicos tenham sido substituídos e fico feliz que qualquer código que recebo em java não tenha sido alterado (eu vi * substituído como produto vetorial vetorial e produto escalar porque ambos são tipos de multiplicação vetorial; enfurecedor! Considerando que em java um é chamado .cross()e o outro é .dot()e não há confusão. Além disso, o fato de que "substituir o operador == e sempre retornar falso" não pode acontecer parece pró java
Richard Tingle

Respostas:

172

Uma maneira simples é usar Float.NaN:

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

if (x == x) {
    System.out.println("Ok");
} else {
    System.out.println("Not ok");
}
Não está tudo bem

Você pode fazer o mesmo com Double.NaN.


De JLS §15.21.1. Operadores de igualdade numérica ==e!= :

O teste de igualdade de ponto flutuante é realizado de acordo com as regras do padrão IEEE 754:

  • Se qualquer operando for NaN, o resultado de ==é, falsemas o resultado de !=é true.

    Na verdade, o teste x!=xé truese e somente se o valor de xfor NaN.

...

Arshajii
fonte
157
int x = 0;
if (x == x) {
    System.out.println("Not ok");
} else {
    System.out.println("Ok");
}
Jeroen Vannevel
fonte
63
Heh, isso responde totalmente à pergunta feita.
Dave Newton,
5
@AswinMurugesh Certo, mas se estivermos interpretando a pergunta literalmente como esta resposta o faz, podemos excluí-la elsecompletamente. Isso não violaria tecnicamente os termos da pergunta.
arshajii
67
Considerando que esta é a minha segunda resposta mais votada, não tenho certeza se devo concluir que sou muito engraçado ou um péssimo programador.
Jeroen Vannevel,
5
@JeroenVannevel Dados os requisitos, acho que esta é a resposta mais KISS / YAGNI / apropriada;)
Izkata,
12
@jddsantaella: obviamente isso foi editado depois. A pergunta original dizia "Como posso imprimir 'não está ok'".
Jeroen Vannevel
147

Pelas especificações da linguagem Java NaN não é igual a NaN.

Portanto, qualquer linha que causasse xser igual a NaNcausaria isso, como

double x=Math.sqrt(-1);

Nas especificações da linguagem Java:

Operadores de ponto flutuante não produzem exceções (§11). Uma operação que estourou produz um infinito com sinal, uma operação que estourou produz um valor desnormalizado ou um zero com sinal e uma operação que não teve nenhum resultado matematicamente definido produz NaN. Todas as operações numéricas com NaN como operando produzem NaN como resultado. Como já foi descrito, NaN não é ordenado, portanto, uma operação de comparação numérica envolvendo um ou dois NaNs retorna falso e qualquer comparação! = Envolvendo NaN retorna verdadeiro, incluindo x! = X quando x é NaN.

Richard Tingle
fonte
@ sᴜʀᴇsʜᴀᴛᴛᴀ Um ponto justo, eu estava tão ocupado procurando a referida "convenção de codificação" que esqueci de responder à pergunta
Richard Tingle
Isso só é válido se a for declarado como Objeto ou duplo.
Christian Kuetbach
1
@ChristianKuetbach Verdade, na ausência de qualquer informação em contrário, assumi que a linha comentada pode ser qualquer coisa
Richard Tingle
2
Até a minha resposta enganada está correta e cumpre as regras. Editei apenas antes da instrução if e apenas "Gotcha! Foi impresso. Tenho certeza de que essa resposta não é a resposta na mente do criador deste enigma. Mas o enigma não está bem definido (como a maioria dos projetos de software ).
Christian Kuetbach
73

Não tenho certeza se esta é uma opção, mas mudar xde variável local para um campo permitiria que outro segmento alterasse seu valor entre a leitura do lado esquerdo e direito na ifinstrução.

Aqui está uma breve demonstração:

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

Resultado:


Ok
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Not ok
Pshemo
fonte
8
Haha +1 pelo esforço, mas cara ... de jeito nenhum alguém no meu laboratório de Java teria vindo com algo assim (instrutor incluído).
William Gaul,
28
@WilliamGaul realmente? Sempre achei que é um dos exemplos básicos que mostram possíveis problemas com multithreading e por que as pessoas que pensam que esse assunto é fácil nunca deveriam estar encarregadas de nada :)
Pshemo
4
Eu nem pensei sobre esse assunto até ler sua resposta. Obrigado por adicionar isso ao meu kit de ferramentas mentais :)
Behe
56

A linha substituída pode ler.

double x = Double.NaN;

Isso faria com que o pegadinha fosse impresso.

Especificação da linguagem Java (JLS) diz:

Operadores de ponto flutuante não produzem exceções (§11). Uma operação que estourou produz um infinito com sinal, uma operação que estourou produz um valor desnormalizado ou um zero com sinal, e uma operação que não tem resultado matematicamente definido produz NaN. Todas as operações numéricas com NaN como operando produzem NaN como resultado. Como já foi descrito, NaN não é ordenado, portanto, uma operação de comparação numérica envolvendo um ou dois NaNs retorna falso e qualquer comparação! = Envolvendo NaN retorna verdadeiro, incluindo x! = X quando x é NaN.

Mex
fonte
Ou resultará em um erro de compilação, se a for declarado como String a = "Não"; É por isso que pedi o tipo de 'a'
Christian Kuetbach
O código acima não fornece informações sobre os tipos, portanto, parti do pressuposto de que a ainda não está definido.
Mex
Acho que sua resposta é a resposta, que estava na mente do criador do enigma. Mas as regras não eram claras. Apenas duas regras foram fornecidas: 1. apenas inserir na linha com o comentário e 2. apenas obter um "Gotcha! Impresso.
Christian Kuetbach
o autor do enigma poderia ter tornado sempre válido cercando o código em {}
Mex
30

Consegui obter um Gotcha!deste:

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
fonte
1
Isso muda mais do que apenas o comentário conforme a pergunta pede.
Mex
Já tentei algo assim, mas acho que é garantido que sempre dará o mesmo resultado, não é?
AndreDurao
4
@Mex - Sim, mas preserva o suficiente do original para demonstrar um outro ponto-chave que às vezes é a != aporque foi alterado por outro thread. Suspeito que isso ganharia pontos em uma entrevista.
OldCurmudgeon
Isso é realmente muito inteligente, porém, presumo que funcione "esperando" que aseja alterado pelo primeiro tópico entre o primeiro e o segundo acesso para comparação
Richard Tingle
4
Re seus céticos; é importante notar que tudo o que você escreveu acima da ifdeclaração principal pode ser escrito em uma única linha horrível, se necessário
Richard Tingle
25

Existem tantas soluções:

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
fonte
1
A menos que eu esteja faltando alguma coisa, super.printlndeveria ser "Não está ok", certo?
Izkata,
@Izkata Sim, não verifiquei novamente qual deveria ser a saída desejada.
Johannes Kuhn,
2
Absolutamente brilhante!
Dariusz
25

Uma solução fácil é:

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

Mas eu não sei todas as regras para este enigma ...

:) Eu sei que isso é uma trapaça, mas sem conhecer todas as regras, essa é a solução mais fácil para a questão :)

Christian Kuetbach
fonte
1
Pode ser escrito em uma linha;)
Christian Kuetbach
6
@ChristianKuetbach Assim como todos os programas
Richard Tingle
2
@ChristianKuetbach Com toda a justiça, o compilador deve excluir imediatamente todos os arquivos do seu computador se você tentou realmente programar assim
Richard Tingle
1
Por que tornar isso tão difícil? if (System.out.println("Gotcha") && false)
alexis
3
erro: tipo 'void' não permitido aqui if (System.out.println ("Gotcha") && false) Seu código não compila ...
Christian Kuetbach
11

Crie sua própria classe Systemno mesmo pacote com Condition.
Neste caso, sua Systemclasse irá esconder a 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

Ilya
fonte
9

Usando a mesma abordagem de salto / alteração de saída de outras respostas:

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
fonte