Чи може змінна Java відрізнятися від самої себе?


106

Мені цікаво, чи можна це питання вирішити на 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умову помилковою), не змінюючи саму умову?


12
Я вважаю, що повинно бути більше обмежень, інакше воно занадто відкрите.
Маленький студент Ферма

52
Це так просто, як System.out.println("Gotcha!");замість коментаря? :)
stuXnet

7
Добре, тоді подвійний а = Double.NaN найкоротша відповідь, і мій "хак - це просто обман;)
Крістіан Куетбах

47
Це милі дрібниці Java, але я сподіваюся, що ніхто не буде розглядати питання про інтерв'ю. Люди, які розглядають кандидатів на роботу, повинні робити все можливе, щоб зрозуміти, чи розуміє кандидат програмування, а не скільки дрібниць, які він накопичив. Я ледве використовував числа з плаваючою комою за 17 років програмування на Java, набагато менше конструкції NaN, МНОГО менше знаючи, як він поводиться з оператором == ...
arcy

8
@ user1158692 Що стосується думки, я особисто ненавиджу будь-які програми, де базові оператори були переохолоджені, і я радий, що будь-який код, який я отримую в Java, не був зіпсований (я бачив * переосмислений як векторний перехресний продукт і крапковий продукт, оскільки обидва є різновидом векторного розмноження; озлоблення! Тоді як у Java один називається, .cross()а другий є, .dot()і не виникає плутанини. Також факт, що "переосмислити оператора == і завжди повертається помилковим", не може відбутися, здається, pro java
Річард Тінгл

Відповіді:


172

Одним із простих способів є використання Float.NaN:

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

if (x == x) {
    System.out.println("Ok");
} else {
    System.out.println("Not ok");
}
Не добре

Ви можете зробити те ж саме і з Double.NaN.


З JLS §15.21.1. Оператори чисельної рівності ==та!= :

Тестування рівності з плаваючою комою проводиться відповідно до правил стандарту IEEE 754:

  • Якщо будь-який операнд - NaN, то результат ==є, falseале результат !=є true.

    Дійсно, тест x!=x- це trueтоді і лише тоді, коли значення xNaN.

...


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

63
Хе, це повністю відповідає на запитання.
Дейв Ньютон

5
@AswinMurugesh Правильно, але якщо ми сприймаємо питання повністю буквально, як це відповідає, то ми можемо видалити elseцілком. Це технічно не порушує умови питання.
Аршаджі

67
Враховуючи, що це моя друга найвища відповідь, я не впевнений, чи можна зробити висновок, що я дуже смішний, або хитрий програміст.
Jeroen Vannevel

5
@JeroenVannevel З огляду на вимоги, я думаю, що це найбільш відповідь KISS / YAGNI / відповідна відповідь;)
Izkata

12
@jddsantaella: очевидно, це було відредаговано згодом. В оригінальному запитанні було сказано "Як я можу надрукувати" не нормально "".
Jeroen Vannevel

147

За специфікаціями мови Java NaN не дорівнює NaN.

Тому будь-який рядок, який викликав xрівність NaN, спричинив би це, наприклад

double x=Math.sqrt(-1);

Із специфікацій мови Java:

Оператори з плаваючою комою не створюють винятків (§11). Операція, що переповнює, створює підписану нескінченність, операція, що перетікає, виробляє денормалізоване значення або підписаний нуль, а операція, яка не має математично визначеного результату, виробляє NaN. Усі числові операції з NaN як операндом дають NaN як результат. Як уже було описано, NaN є не упорядкованим, тому чисельна операція порівняння, що включає один або два NaN, повертає помилкові і будь-які! = Порівняння, що включає NaN, повертає істинне, включаючи x! = X, коли x - NaN.


@ sᴜʀᴇsʜᴀᴛᴛᴀ Справедливий момент, я був настільки зайнятий, щоб піти, щоб знайти сказане "конвенція про кодування", що я забув насправді відповісти на питання
Річард Тінгл

Це справедливо лише в тому випадку, якщо a оголошено як Object або double.
Крістіан Куетбах

1
@ChristianKuetbach Щоправда, за відсутності будь-якої інформації, яка суперечить, я припустив, що коментована лінія може бути будь-чим
Річард Тінгл

2
Навіть моя обдурена відповідь правильна і відповідає правилам. Я редагував лише перед твердженням if і надруковано лише "Gotcha!". Я впевнений, що ця відповідь не є відповіддю в розумі творця цієї загадки. Але загадка недостатньо чітко визначена (як і більшість програмних проектів ).
Крістіан Куетбах

73

Не впевнений, що це варіант, але зміна 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

8
Ха-ха +1 за зусилля, але людино ... немає ніякого способу, щоб хтось із моєї лабораторії Java придумав щось подібне (інструктор включений).
Вільям Галл

28
@WilliamGaul Дійсно? Я завжди хоч, що це один з основних прикладів, що показують можливі проблеми з багатопотоковою програмою, і чому люди, які думають, що ця тема є легкою, ніколи ні про що не повинні відповідати :)
Pshemo

4
Я навіть не замислювався над цим питанням, поки не прочитав вашої відповіді. Дякую, що додали це до мого розумового інструментарію :)
Behe

56

Замінений рядок міг прочитати.

double x = Double.NaN;

Це призведе до друку ґутчі.

Специфікація мови Java (JLS) говорить:

Оператори з плаваючою комою не створюють винятків (§11). Операція, що переповнює, створює підписану нескінченність, операція, що перетікає, виробляє денормалізоване значення або підписаний нуль, а операція, яка не має математично визначеного результату, виробляє NaN. Усі числові операції з NaN як операндом дають NaN як результат. Як уже було описано, NaN є не упорядкованим, тому чисельна операція порівняння, що включає один або два NaN, повертає помилкові і будь-які! = Порівняння, що включає NaN, повертає істинне, включаючи x! = X, коли x - NaN.


Або призвести до помилки компіляції, якщо a оголошено як String a = "Nope"; Ось чому я попросив тип "а"
Крістіан Куетбах

Код, наведений вище, не містить інформації про типи, тому я зробив припущення, що а ще не визначений.
Мекс

Я думаю, що ваша відповідь - це відповідь, що була у думці творця загадки. Але правила були не зрозумілі. Дано лише два правила: 1. Вставте лише рядок із коментарем та 2. Отримайте лише одне "Отримано!"
Крістіан Кутбах

автор загадки міг би бути завжди дійсним, оточуючи код у {}
Мексиці

30

Мені вдалося отримати 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;
    }
  }
}

1
Це змінює більше, ніж просто коментар, оскільки запитання задається.
Мекс

Я пробував щось подібне, але думаю, що це гарантовано, що це завжди буде мати однаковий результат, чи не так?
AndreDurao

4
@Mex - Дійсно, але він зберігає достатньо оригіналу, щоб продемонструвати ще один ключовий момент, який іноді a != aчерез те, що він був змінений іншим потоком. Я підозрюю, що це виграє бали в інтерв'ю.
OldCurmudgeon

Це насправді досить розумно, але я припускаю, що це працює "сподіваючись", що aце змінено першим потоком між першим і другим доступом для порівняння
Річард Тінгл

4
Re ваші сумніви; Варто зазначити, що все, що ви написали над ключовою ifзаявою, за потреби можна було написати одним жахливим рядком
Річард Тінгл

25

Є так багато рішень:

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
Якщо я щось не пропускаю, це super.printlnповинно бути "Не нормально", правда?
Ізката

@Izkata Так, не повторно перевірити, яким повинен бути бажаний вихід.
Йоханнес Кун

2
Абсолютно геніально!
Даріуш

25

Одне просте рішення:

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

Але я не знаю всіх правил цієї загадки ...

:) Я знаю, що це чіт, але не знаючи всіх правил, це найпростіший варіант вирішення питання :)


1
Можна записати одним рядком;)
Крістіан Куетбах

6
@ChristianKuetbach Як і всі програми
Richard Tingle

2
@ChristianKuetbach Якщо чесно, компілятор повинен негайно видалити всі файли на комп’ютері, якщо ви намагалися насправді програмувати так
Річард Тінгл

1
Чому це зробити так важко? if (System.out.println("Gotcha") && false)
alexis

3
помилка: тип "void" тут не дозволено, якщо (System.out.println ("Gotcha") & false) Ваш код не буде компілюватися ...
Christian Kuetbach

11

Створіть свій власний клас 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 DEMO


9

Використовуючи той же підхід пропуску / зміни виводу з інших відповідей:

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");
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.