Czym zmienna Java może się różnić od siebie?

106

Zastanawiam się, czy to pytanie można rozwiązać w Javie (jestem nowy w tym języku). To jest kod:

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

W moim laboratorium otrzymałem następujące pytanie: Jak można pominąć pierwszy przypadek (tj. Uczynić x == xwarunek fałszywym) bez modyfikowania samego warunku?

Husam
źródło
12
Uważam, że powinno być więcej ograniczeń, w przeciwnym razie jest zbyt otwarta.
Mały uczeń Fermata,
52
Czy to tak proste, jak System.out.println("Gotcha!");zamiast komentarza? :)
stuXnet
7
Dobrze, więc jest podwójne a = Double.NaN najkrótsza odpowiedź, a mój „hack to tylko oszustwo;)
Christian Kuetbach
47
To urocze ciekawostki dotyczące języka Java, ale mam nadzieję, że nikt nie rozważy zadawania tego pytania podczas wywiadu. Osoby rozważające kandydatów do pracy powinny robić wszystko, co w ich mocy, aby dowiedzieć się, czy kandydat rozumie programowanie, a nie ile informacji zgromadził. Ledwo używałem liczb zmiennoprzecinkowych przez 17 lat programowania w Javie, a tym bardziej konstrukcji NaN, DUŻO mniej wiedząc, jak zachowuje się z operatorem == ...
arcy
8
@ user1158692 Kwestia opinii, osobiście nienawidzę programów, w których podstawowe operatory zostały zastąpione i cieszę się, że żaden kod, który otrzymałem w Javie, nie został zepsuty (widziałem * nadpisane jako iloczyn wektorowy i iloczyn skalarny, ponieważ oba są rodzajem mnożenia wektorów; irytujące! Podczas gdy w Javie jeden jest wywoływany, .cross()a drugi jest, .dot()i nie ma zamieszania. Również fakt, że „nadpisuj operator == i zawsze zwracaj fałsz” nie może się zdarzyć, wydaje się pro java
Richard Tingle

Odpowiedzi:

172

Jednym prostym sposobem jest użycie Float.NaN:

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

if (x == x) {
    System.out.println("Ok");
} else {
    System.out.println("Not ok");
}
Nie w porządku

Możesz zrobić to samo z Double.NaN.


Z JLS §15.21.1. Liczbowe operatory równości ==i!= :

Testowanie zmiennoprzecinkowe równości wykonywane jest zgodnie z zasadami normy IEEE 754:

  • Jeśli którykolwiek z operandów jest NaN, to wynik ==jest, falseale wynik !=jest true.

    Rzeczywiście, test x!=xjest truewtedy i tylko wtedy, gdy wartość xwynosi NaN.

...

arshajii
źródło
157
int x = 0;
if (x == x) {
    System.out.println("Not ok");
} else {
    System.out.println("Ok");
}
Jeroen Vannevel
źródło
63
Heh, to całkowicie odpowiada na zadane pytanie.
Dave Newton
5
@AswinMurugesh Zgadza się, ale jeśli traktujemy pytanie całkowicie dosłownie, tak jak w przypadku tej odpowiedzi, możemy elsecałkowicie usunąć . Technicznie rzecz biorąc, nie naruszyłoby to warunków pytania.
arshajii
67
Biorąc pod uwagę, że jest to moja druga najwyżej oceniona odpowiedź, nie jestem pewien, czy stwierdzić, że jestem bardzo zabawny, czy kiepskim programistą.
Jeroen Vannevel,
5
@JeroenVannevel Biorąc pod uwagę wymagania, myślę, że jest to najbardziej KISS / YAGNI / odpowiednia odpowiedź;)
Izkata
12
@jddsantaella: oczywiście to było później edytowane. Pierwotne pytanie brzmiało: „Jak mogę wydrukować„ źle ””.
Jeroen Vannevel
147

Według specyfikacji języka Java NaN nie jest równa NaN.

Dlatego każda linia, która spowodowała, że xbyłaby równa NaN, spowodowałaby to, na przykład

double x=Math.sqrt(-1);

Ze specyfikacji języka Java:

Operatory zmiennoprzecinkowe nie powodują żadnych wyjątków (§11). Operacja, która się przepełnia, tworzy nieskończoność ze znakiem, operacja, która powoduje niedomiar, wytwarza zdenormalizowaną wartość lub zero ze znakiem, a operacja, która nie ma matematycznie określonego wyniku, daje NaN. Wszystkie operacje numeryczne z NaN jako operandem dają wynik NaN. Jak już wspomniano, NaN jest nieuporządkowany, więc operacja numerycznego porównania obejmująca jeden lub dwa NaN zwraca fałsz, a każde! = Porównanie obejmujące NaN zwraca prawdę, w tym x! = X, gdy x jest NaN.

Richard Tingle
źródło
@ sᴜʀᴇsʜᴀᴛᴛᴀ Słuszna uwaga, byłem tak zajęty szukaniem wspomnianej „konwencji kodowania”, że zapomniałem odpowiedzieć na pytanie
Richard Tingle
Jest to ważne tylko wtedy, gdy a jest zadeklarowane jako Object lub double.
Christian Kuetbach
1
@ChristianKuetbach Prawda, przy braku jakichkolwiek informacji przeciwnych założyłem, że zakomentowana linia może być dowolna
Richard Tingle
2
Nawet moja oszukana odpowiedź jest poprawna i spełnia zasady. Edytowałem tylko przed instrukcją if i drukowane jest tylko „Gotcha! )
Christian Kuetbach
73

Nie jestem pewien, czy jest to opcja, ale zmiana xzmiennej lokalnej na pole umożliwiłaby innemu wątkowi zmianę wartości między odczytem lewej i prawej strony ifinstrukcji.

Oto krótkie demo:

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

Wynik:

⋮
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Ok
Not ok
Pshemo
źródło
8
Haha +1 za wysiłek, ale człowieku ... nie ma mowy, żeby ktokolwiek z mojego laboratorium Java wymyślił coś takiego (w tym instruktor).
William Gaul
28
@WilliamGaul Naprawdę? Zawsze myślałem, że jest to jeden z podstawowych przykładów pokazujących możliwe problemy z wielowątkowością i dlaczego ludzie, którzy uważają, że ten temat jest łatwy, nigdy nie powinni się niczym
zajmować
4
Nawet nie myślałem o tym problemie, dopóki nie przeczytałem Twojej odpowiedzi. Dziękuję za dodanie tego do mojego zestawu narzędzi mentalnych :)
Behe
56

Zastąpiona linia mogła czytać.

double x = Double.NaN;

Spowodowałoby to wydrukowanie gotcha.

Specyfikacja języka Java (JLS) mówi:

Operatory zmiennoprzecinkowe nie powodują żadnych wyjątków (§11). Operacja, która się przepełnia, tworzy nieskończoność ze znakiem, operacja, która powoduje niedomiar, wytwarza zdenormalizowaną wartość lub zero ze znakiem, a operacja, która nie ma matematycznie określonego wyniku, daje NaN. Wszystkie operacje numeryczne z NaN jako operandem dają wynik NaN. Jak już wspomniano, NaN jest nieuporządkowany, więc operacja numerycznego porównania obejmująca jeden lub dwa NaN zwraca fałsz, a każde! = Porównanie obejmujące NaN zwraca prawdę, w tym x! = X, gdy x jest NaN.

Mex
źródło
Lub prowadzić do błędu kompilacji, jeśli a jest zadeklarowane jako String a = "Nope"; Dlatego zapytałem o typ „a”
Christian Kuetbach
Powyższy kod nie podaje żadnych informacji o typach, dlatego przyjąłem założenie, że a nie jest już zdefiniowane.
Meksyk
Myślę, że twoja odpowiedź jest odpowiedzią, która była w umyśle twórcy zagadek. Ale zasady nie były jasne. Podano tylko dwie zasady: 1. wstaw tylko w wierszu z komentarzem i 2. wydrukuj tylko jedno "Gotcha!".
Christian Kuetbach
autor zagadki mógł zawsze sprawić, by był ważny, otaczając kod w {}
Mex
30

Udało mi się uzyskać Gotcha!z tego:

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
źródło
1
To zmienia coś więcej niż tylko komentarz, o który prosi się pytanie.
Meksyk
Próbowałem czegoś takiego, ale myślę, że jest to gwarantowane, że zawsze da taki sam rezultat, prawda?
AndreDurao
4
@Mex - rzeczywiście, ale zachowuje wystarczająco dużo oryginału, aby zademonstrować kolejny kluczowy punkt, który czasami a != azostał zmieniony przez inny wątek. Podejrzewam, że to dałoby punkty w wywiadzie.
OldCurmudgeon
Jest to jednak całkiem sprytne, zakładam, że to działa na zasadzie "nadziei", że ajest ona zmieniana przez pierwszy wątek pomiędzy pierwszym a drugim dostępem do porównania
Richard Tingle
4
Ponownie wątpiący; warto zauważyć, że wszystko, co napisałeś powyżej kluczowego ifstwierdzenia, może być zapisane w jednej okropnej linii, jeśli to konieczne
Richard Tingle
25

Rozwiązań jest tak wiele:

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
źródło
1
Chyba że czegoś brakuje, to super.printlnpowinno być „Nie w porządku”, prawda?
Izkata
@Izkata Tak, nie sprawdziłem ponownie, jakie powinno być pożądane wyjście.
Johannes Kuhn
2
Absolutnie genialne!
Dariusz
25

Jednym prostym rozwiązaniem jest:

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

Ale nie znam wszystkich zasad tej zagadki ...

:) Wiem, że to oszustwo, ale bez znajomości wszystkich zasad to najłatwiejsze rozwiązanie na pytanie :)

Christian Kuetbach
źródło
1
Można zapisać w jednej linii;)
Christian Kuetbach
6
@ChristianKuetbach Jak wszystkie programy
Richard Tingle
2
@ChristianKuetbach Z całą uczciwością kompilator powinien natychmiast usunąć wszystkie pliki z twojego komputera, jeśli próbowałeś faktycznie programować w ten sposób
Richard Tingle
1
Po co to tak utrudniać? if (System.out.println("Gotcha") && false)
Alexis
3
błąd: typ 'void' jest tu niedozwolony, jeśli (System.out.println ("Gotcha") && false) Twój kod się nie skompiluje ...
Christian Kuetbach
11

Utwórz własną klasę Systemw tym samym pakiecie z Condition.
W takim przypadku Twoja Systemklasa ukryje java.lang.Systemklasę

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
źródło
9

Używając tego samego podejścia pomiń / zmień dane wyjściowe z innych odpowiedzi:

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
źródło