Как переменная Java может отличаться от самой себя?

Мне интересно, можно ли решить этот вопрос на Java (я новичок в этом языке). Это код:

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

Я получил следующий вопрос в моей лаборатории: Как вы можете пропустить первый случай (т.е. сделать условие x == x ложным) без изменения самого условия?


person Husam    schedule 17.10.2013    source источник
comment
Я считаю, что должно быть больше ограничений, иначе это слишком открыто.   -  person Fermat's Little Student    schedule 17.10.2013
comment
Какие части кода вы можете изменить, если таковые имеются?   -  person Raedwald    schedule 17.10.2013
comment
comment
Это так же просто, как System.out.println("Gotcha!"); вместо комментария? :)   -  person stuXnet    schedule 04.12.2013
comment
Какой тип а? Строковый объект двойной?   -  person Christian Kuetbach    schedule 04.12.2013
comment
Какой бы тип вы хотите;   -  person AndreDurao    schedule 04.12.2013
comment
Хорошо, тогда double a= Double.NaN — самый короткий ответ, а мой хак — просто чит ;)   -  person Christian Kuetbach    schedule 04.12.2013
comment
Это милая Java-викторина, но я надеюсь, что никто не подумает о том, чтобы сделать ее вопросом на собеседовании. Люди, рассматривающие кандидатов на работу, должны делать все возможное, чтобы выяснить, понимает ли кандидат программирование, а не то, сколько мелочей он накопил. Я почти не использовал числа с плавающей запятой за 17 лет программирования на Java, а тем более конструкцию NaN, НАМНОГО меньше зная, как она ведет себя с оператором ==...   -  person arcy    schedule 04.12.2013
comment
stackoverflow.com/questions/471296/ и stackoverflow.com/questions/18460416/   -  person Alvin Wong    schedule 04.12.2013
comment
Чуть больше было бы определить класс, который переопределяет оператор == и всегда возвращает false, а затем создает экземпляр этого класса как a.   -  person Grimm The Opiner    schedule 04.12.2013
comment
@user1158692 user1158692 В Java вы не можете переопределить базовые операторы, такие как ==   -  person Richard Tingle    schedule 04.12.2013
comment
@user1158692 user1158692 Отправь это в slashdot.   -  person millimoose    schedule 04.12.2013
comment
@user1158692 user1158692 Дело в том, что лично я ненавижу любые программы, в которых основные операторы были переопределены, и рад, что любой код, который я получаю в java, не был испорчен (я видел, что * переопределен как векторное перекрестное произведение и точечный продукт, потому что оба являются видами векторного умножения; бесит! В то время как в java один называется .cross(), а другой - .dot(), и нет никакой путаницы. Также тот факт, что переопределяется оператор == и всегда возвращается false не может случиться кажется про java   -  person Richard Tingle    schedule 04.12.2013
comment
иногда это может сбивать с толку, но чтение myObj == yourObj немного яснее, чем myObj.equals(yourObj).   -  person AndreDurao    schedule 04.12.2013
comment
@AndreDurao Ах, но в java myObj == yourObj означает действительно и точно один и тот же объект, тогда как .equals() означает, что он выглядит одинаково, это разные понятия, и нам нужны они оба   -  person Richard Tingle    schedule 04.12.2013
comment
@rcook очень важно знать о NaN, откуда он взялся и как себя ведет. Он есть почти в каждом крупном языке (и во всех он ведет себя одинаково), и его неправильное выполнение может привести к добавлению очень хитрых ошибок в любой код, выполняющий нецелочисленные вычисления.   -  person OrangeDog    schedule 04.12.2013
comment
@RichardTingle, вы можете так же легко переопределить метод .equals(), чтобы всегда return false;   -  person clcto    schedule 04.12.2013
comment
в дополнение к данным ответам есть это   -  person eis    schedule 04.12.2013
comment
И после объединения вопросов никто не может понять, о чем все говорят в этом посте.   -  person Lol4t0    schedule 11.12.2013
comment
@RichardTingle нет абсолютно никакой путаницы, когда дело доходит до перегруженного векторного перекрестного и точечного умножения, даже если вы добавите в смесь матричное умножение: сигнатуры достаточно разные, поэтому всегда очевидно, какая форма умножения применяется. Тем не менее, какой язык вообще допускает такую ​​перегрузку? Это звучало как удар по C++, но в C++ у вас не может быть двух перегрузок с одним и тем же аргументом и разными типами результатов...   -  person Magnus    schedule 11.12.2013
comment
@Magnus Я имею в виду, что одна библиотека перегружается в одну сторону, а другая библиотека перегружается в другую сторону, и я говорю о C++. И это только пример; дело в том, что мне нравится знать, что делают мои функции, только из названия; документы должны поддерживать это, а не быть обязательными   -  person Richard Tingle    schedule 11.12.2013
comment
@RichardTingle Понятно ... ну, с математической точки зрения вполне разумно использовать оператор * для скалярного произведения, потому что именно так определяются векторные пространства практически в любом учебнике по линейной алгебре, и я утверждал, что задействованные типы имеют тенденцию делать это очень ясно, что делает операция. Я знаю, что это всего лишь пример, но я, честно говоря, не вижу путаницы, потому что я действительно думаю, что в этом контексте это очень ясно, и это делает код намного легче для чтения. Не могу даже придумать действительно ужасного примера.   -  person Magnus    schedule 11.12.2013
comment
Я рад, что наткнулся на это, так что я могу знать, как не задавать абсурдный взгляд, как smRT я спрашиваю как инструктор. Я бы сильно усомнился в продуктивности человека, который разработал этот вопрос - о, подождите, это учитель...   -  person kingdango    schedule 12.12.2013


Ответы (10)


Одним из простых способов является использование Float.NaN. :

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

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

Вы можете сделать то же самое с Double.NaN. .


Из JLS §15.21 .1. Операторы числового равенства == и !=:

Проверка равенства с плавающей запятой выполняется в соответствии с правилами стандарта IEEE 754:

  • Если любой из операндов равен NaN, то результатом == будет false, а результатом != будет true.

    Действительно, тест x!=x равен true тогда и только тогда, когда значение x равно NaN.

...

person arshajii    schedule 17.10.2013

Согласно Спецификациям языка Java NaN не равно NaN.

Следовательно, любая строка, в которой x равняется NaN, вызовет это, например

double x=Math.sqrt(-1);

Из спецификаций языка Java:

Операторы с плавающей запятой не создают исключений (§11). Операция, которая переполняется, дает бесконечность со знаком, операция, которая теряет значение, дает денормализованное значение или ноль со знаком, а операция, которая не имеет математически определенного результата, дает NaN. Все числовые операции с NaN в качестве операнда дают в результате NaN. Как уже было сказано, числовое значение NaN неупорядочено, поэтому операция числового сравнения, включающая одно или два значения NaN, возвращает false, а любое сравнение !=, включающее число NaN, возвращает значение true, включая x!=x, когда x равно NaN.

person Richard Tingle    schedule 04.12.2013
comment
@sᴜʀᴇsʜᴀᴛᴛᴀ Справедливое замечание, я был так занят поиском указанного соглашения о кодировании, что забыл ответить на вопрос - person Richard Tingle; 04.12.2013
comment
Это допустимо только в том случае, если объявлено как Object или double. - person Christian Kuetbach; 04.12.2013
comment
@ChristianKuetbach Правда, в отсутствие какой-либо информации об обратном я предположил, что закомментированная строка может быть чем угодно - person Richard Tingle; 04.12.2013
comment
Даже мой обманутый ответ правильный и соответствует правилам. Я редактировал только перед оператором if и только Gotcha! распечатывается. Я уверен, что этот ответ не является ответом в уме создателя этой загадки. Но загадка не определена четко (как и большинство программных проектов). - person Christian Kuetbach; 04.12.2013

Не уверен, что это вариант, но изменение x из локальной переменной в поле позволит другому потоку изменить свое значение между чтением слева и справа в операторе if.

Вот короткая демонстрация:

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

Вывод:

⋮
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Not ok
person Pshemo    schedule 17.10.2013
comment
Ха-ха +1 за усилия, но чувак... никто в моей Java-лаборатории не придумал бы что-то подобное (включая инструктора). - person William Gaul; 17.10.2013
comment
@WilliamGaul Правда? Я всегда думал, что это один из основных примеров, показывающих возможные проблемы с многопоточностью и почему люди, которые думают, что эта тема проста, никогда не должны отвечать за что-либо :) - person Pshemo; 17.10.2013

Замененная строка может читаться.

double x = Double.NaN;

Это приведет к тому, что Gotcha будет напечатан.

Спецификация языка Java (JLS) гласит:

Операторы с плавающей запятой не создают исключений (§11). Операция, которая переполняется, дает бесконечность со знаком, операция, которая теряет значение, дает денормализованное значение или ноль со знаком, а операция, которая не имеет математически определенного результата, дает NaN. Все числовые операции с NaN в качестве операнда дают в результате NaN. Как уже было сказано, NaN является неупорядоченным, поэтому операция числового сравнения с одним или двумя значениями NaN возвращает false, а любое сравнение != с участием NaN возвращает значение true, включая x!=x, когда x равно NaN.

person Mex    schedule 04.12.2013
comment
Или привести к ошибке компиляции, если a объявлено как String a = Nope; Вот почему я попросил тип «а» - person Christian Kuetbach; 04.12.2013
comment
Приведенный выше код не дает информации о типах, поэтому я сделал предположение, что a еще не определен. - person Mex; 04.12.2013
comment
Я думаю, что ваш ответ - это ответ, который был в уме создателя загадки. Но правила были не ясны. Было дано только два правила: 1. вставить только в строку с комментарием и 2. получить только одно Gotcha! распечатанный. - person Christian Kuetbach; 04.12.2013
comment
автор загадки мог бы сделать всегда действительным, заключив код в { } - person Mex; 04.12.2013

Мне удалось получить Gotcha! из этого:

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;
    }
  }
}
person OldCurmudgeon    schedule 04.12.2013
comment
Это меняет не только комментарий, как того требует вопрос. - person Mex; 04.12.2013
comment
Я пробовал что-то подобное, но я думаю, что гарантировано, что он всегда будет давать один и тот же результат, не так ли? - person AndreDurao; 04.12.2013
comment
@Mex - это действительно так, но он сохраняет достаточно оригинала, чтобы продемонстрировать еще один ключевой момент, который иногда a != a, потому что он был изменен другим потоком. Я подозреваю, что это принесет очки в интервью. - person OldCurmudgeon; 04.12.2013
comment
На самом деле это довольно умно, я предполагаю, что это работает, надеясь, что a изменяется первым потоком между первым и вторым доступом для сравнения. - person Richard Tingle; 04.12.2013
comment
@RichardTingle - Не просто надежда - у меня появилась настоящая надежда, когда я запустил ее на своем многоядерном ПК. - person OldCurmudgeon; 04.12.2013
comment
Я думаю, что это решение противоречит правилам. Там написано Вместо этого комментария... Итак, я думаю, вам не разрешили изменить следующий код. - person Christian Kuetbach; 04.12.2013
comment
@OldCurmudgeon Ах, но я предполагаю, что теоретически эта программа может работать вечно, не нажимая на это. Просто очень вероятно, что он быстро ударит - person Richard Tingle; 04.12.2013
comment
@ChristianKuetbach Совершенно верно, но я поставлю +1 просто за изобретательность. - person Richard Tingle; 04.12.2013
comment
Re ваши сомневающиеся; стоит отметить, что все, что вы написали над оператором ключа if, можно было бы записать в одну ужасную строку, если это необходимо - person Richard Tingle; 04.12.2013
comment
@RichardTingle Я думаю, что это предполагает, что этот код находится внутри метода. Если вы добавите к этому заголовки классов и методов, вам придется добавить дополнительный код после упомянутого кода, даже если это будут фигурные скобки, что противоречит правилам. Любая программа, содержащая этот блок if, которая скомпилировалась бы, если бы существовала, не скомпилировалась бы, если бы вы вставили сюда то, что над ней. Возможно, если бы вы сделали несколько анонимных классов внутри, метод сработал бы. В любом случае, я проголосовал за это. Это мой любимый ответ и первое, о чем я подумал, читая задачу. - person Cruncher; 04.12.2013

Решений очень много:

import java.io.PrintStream;

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");
        }
    }
}
person Johannes Kuhn    schedule 17.10.2013
comment
Если я что-то не упустил, то super.println должно быть Не в порядке, верно? - person Izkata; 17.10.2013
comment
@Izkata Да, не перепроверял, каким должен быть желаемый результат. - person Johannes Kuhn; 17.10.2013

Одно простое решение:

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

Но я не знаю всех правил этой загадки...

:) Я знаю, что это чит, но не зная всех правил, это самое простое решение вопроса :)

person Christian Kuetbach    schedule 04.12.2013
comment
Умный ответ, поскольку в комментарии не сказано, КАК ПОЛУЧИЛОСЬ! должен быть напечатан, однако я думаю, что это считается за две строки. :п - person Mex; 04.12.2013
comment
Можно написать в одну строку ;) - person Christian Kuetbach; 04.12.2013
comment
@ChristianKuetbach Как и все программы - person Richard Tingle; 04.12.2013
comment
Я думаю, что оператор switch не будет компилироваться. - person Christian Kuetbach; 04.12.2013
comment
int b=1; switch(b){case 1: System.out.println("woo"); break; case 2: System.out.println("boo");break;} - это одна строка, компилируется и печатает woo (как и все хорошие программы) - person Richard Tingle; 04.12.2013
comment
Да я тоже пробовал. Я думаю, что мой компилятор в мозгу просто останавливается, если код слишком далек от стандартов кодирования.... - person Christian Kuetbach; 04.12.2013
comment
@ChristianKuetbach Справедливости ради, компилятор должен немедленно удалить все файлы на вашем компьютере, если вы попытаетесь на самом деле запрограммировать это - person Richard Tingle; 04.12.2013
comment
Зачем так усложнять? if (System.out.println("Gotcha") && false) - person alexis; 04.12.2013
comment
ошибка: тип 'void' здесь не разрешен, если (System.out.println(Gotcha)&& false) Ваш код не скомпилируется... - person Christian Kuetbach; 04.12.2013
comment
На самом деле это не говорит о том, что вы не можете еще не... распечатать, так что просто напечатайте ошибку, и пусть все остальное работает как обычно. - person dyesdyes; 04.12.2013

Создайте свой собственный класс System в том же пакете, что и Condition.
В этом случае ваш класс System скроет класс java.lang.System

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

person Ilya    schedule 04.11.2013

Используя тот же подход пропуска/изменения вывода из других ответов:

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");
        }
    }
}
person higuaro    schedule 22.10.2013

person    schedule
comment
Хех, это полностью отвечает на заданный вопрос. - person Dave Newton; 17.10.2013
comment
Что ж, если вы выберете этот путь, просто поставьте ; после if и напечатайте "Not ok" напрямую. Однако я не верю, что это то, к чему стремился ОП. - person arshajii; 17.10.2013
comment
@arshajii: Если после if стоит точка с запятой, то в части else будет ошибка. - person Aswin Murugesh; 17.10.2013
comment
@AswinMurugesh Верно, но если мы воспринимаем вопрос буквально, как этот ответ, то мы можем полностью удалить else. Технически это не нарушит условия вопроса. - person arshajii; 17.10.2013
comment
Учитывая, что это мой второй по количеству голосов ответ, я не уверен, сделать ли вывод, что я очень забавный или дрянной программист. - person Jeroen Vannevel; 17.10.2013
comment
@JeroenVannevel Учитывая требования, я думаю, что это самый подходящий ответ KISS/YAGNI/;) - person Izkata; 17.10.2013
comment
Решение проблем — это не программирование. Вы выиграли, потому что решили эту проблему, изменив точку зрения, и я думаю, что это ожидаемый ответ. +1 - person Daniele Vrut; 23.10.2013
comment
Вопрос был в том, как вы можете пропустить первый случай, поэтому этот ответ неверен. - person jddsantaella; 24.10.2013
comment
@jddsantaella: очевидно, это было отредактировано позже. В исходном вопросе говорилось, как я могу напечатать «не в порядке». - person Jeroen Vannevel; 24.10.2013
comment
Вы забыли самую важную и последнюю строчку: // yourQuestion == PWND! - person kingdango; 12.12.2013