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?


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.

...


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

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.


@ 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

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.


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

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

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 :)


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


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");
        }
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.