Чи може змінна 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.