obiekt == null lub null == obiekt?

97

Słyszałem od kogoś, że null == objectjest to lepsze niż object == null sprawdzanie

np .:

void m1(Object obj ) {
   if(null == obj)  // Is this better than object == null ? Why ?
       return ;
   // Else blah blah
}

Czy są jakieś powody, czy to kolejny mit? Dzięki za pomoc.

Jijoy
źródło
Duplikat 271561 i 1957836 (z wyjątkiem tego, że ten mówi „w Javie”)
Gishu
20
Java różni się od C #
Jijoy
5
Jeśli nie było znaczną przewagę wydajności, wtedy na pewno będzie to kompilator optymalizacji ...
Andreas Dolk
W przypadku nullodniesień domyślnym sposobem działania powinno być wyrzucenie NPE. Niektóre fajne biblioteki (takie jak biblioteka JDK7 Java) mają metodę podobną do public static <T> T notNull(T obj) { if (obj == null) { throw new NullPointerException(); } else { return obj; } }. Jest też @NonNull(lub @Nonnull?), Ale to zostaje „wymazane”.
Tom Hawtin - tackline
2
null == objectjest znany jako stan Yody .
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Odpowiedzi:

140

Jest to prawdopodobnie nawyk wyuczony od C, aby uniknąć tego rodzaju literówki (pojedyncza =zamiast podwójna ==):

if (object = null) {

Konwencja umieszczania stałej po lewej stronie ==nie jest tak naprawdę przydatna w Javie, ponieważ Java wymaga, aby wyrażenie w ifewaluacji miało booleanwartość, więc jeśli stała to a boolean, wystąpiłby błąd kompilacji w każdym przypadku argumenty. (a jeśli jest to wartość logiczna, i tak nie powinieneś jej używać ==...)

Laurence Gonsalves
źródło
4
co nie jest bardzo użytecznym nawykiem dla javy, ponieważ literówka spowodowałaby błąd kompilacji w java
radai
11
Z wyjątkiem konkretnego przypadku narożnego. stackoverflow.com/questions/2369226/null-check-in-java/…
Chandra Sekar
3
@Chandru dla wartości logicznej, możesz użyć x.booleanValue()lub !x.booleanValue(). x == truelub x == falsew wyrażeniu warunkowym to nieprzyjemny zapach, IMHO.
Laurence Gonsalves
2
Sprawdza tutaj wartość null. Są przypadki, w których naprawdę musi sprawdzić wartość logiczną pod kątem wartości null, ponieważ null nie jest ani prawdą, ani fałszem.
Chandra Sekar
1
Przyzwyczajenie się do używania null == objecttylko w celu upewnienia się, że nie nadpisujemy obiektu przypadkowo literówką, nie powinno w ogóle być przypadkiem użycia. Co oznacza, że ​​uczymy się, że teraz, gdy najpierw używam „null”, jest w porządku, nawet jeśli popełnię błąd. Nie oznacza to, że kod działa zgodnie z oczekiwaniami. Nawet jeśli null = objectzostanie podany w miejscu null == object, program nie będzie działał zgodnie z oczekiwaniami! Zatem nie ma sensu trzymać się tej konwencji!
VanagaS
33

Jak powiedzieli inni, jest to nawyk, którego nauczył się od C, aby unikać literówek - chociaż nawet w C spodziewałbym się przyzwoitych kompilatorów na wystarczająco wysokich poziomach ostrzegawczych, aby dać ostrzeżenie. Jak mówi Chandru, porównywanie z null w Javie w ten sposób spowodowałoby problemy tylko wtedy, gdybyś używał zmiennej typu Boolean(której nie ma w przykładowym kodzie). Powiedziałbym, że to dość rzadka sytuacja i nie taka, dla której warto zmieniać sposób pisania kodu w innych miejscach. (Nie zawracałbym sobie głowy odwracaniem operandów nawet w tym przypadku; jeśli myślę wystarczająco jasno, aby rozważyć ich odwrócenie, jestem pewien, że mogę policzyć znaki równości).

Co nie zostało wspomniane jest, że wielu ludzi (ja na pewno w zestawie) znaleźć if (variable == constant)formularz, aby być bardziej czytelny - jest to bardziej naturalny sposób wyrażania siebie. Jest to powód, dla którego nie należy ślepo kopiować konwencji z C. Zawsze powinieneś kwestionować praktyki (tak jak to robisz tutaj :), zanim założysz, że to, co może być przydatne w jednym środowisku, jest przydatne w innym.

Jon Skeet
źródło
3
Zgadzam się z każdym słowem, które powiedziałeś, a zwłaszcza z częścią dotyczącą „ślepego kopiowania konwencji”. Czytam teraz kod, a styl null = object denerwuje mnie tak bardzo, że sprawdzam go i przyszedłem tutaj. Co myślał koder?
Adrian M
28

Nie ma to dużej wartości w Javie (1.5+), z wyjątkiem sytuacji, gdy typ obiektu to Boolean. W takim przypadku może to być nadal przydatne.

if (object = null)nie spowoduje błędu kompilacji w Javie 1.5+, jeśli obiekt jest, Booleanale wyrzuci NullPointerExceptionw czasie wykonywania.

Chandra Sekar
źródło
Prawdopodobnie chodziło Ci o to, że spowoduje to błąd kompilacji :)
vava
7
Nie, nie spowoduje to błędu kompilacji, gdy obiekt ma wartość logiczną.
Chandra Sekar
Czy nie jest to takie samo dla każdego typu obiektu oprócz Boolean?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
2
W przypadku typów innych niż logiczne kod nie zostanie skompilowany, ponieważ typ wyrażenia „obiekt = null” nie będzie typu boolowskiego. Z powodu auto-boxingu skompiluje się dla Boolean.
Chandra Sekar
Ale dlaczego to zachowanie dla Boolean? Jakieś uzasadnienie?
Rohit Banga
9

W Javie nie ma dobrego powodu.

Kilka innych odpowiedzi twierdziło, że dzieje się tak, ponieważ możesz przypadkowo przypisać to zadanie zamiast równości. Ale w Javie musisz mieć wartość logiczną w if, więc to:

if (o = null)

nie będzie się kompilować.

W Javie może to mieć znaczenie tylko wtedy, gdy zmienna ma wartość logiczną:

int m1(boolean x)
{
    if (x = true)  // oops, assignment instead of equality
R Samuel Klatchko
źródło
4
Ale dlaczego miałbyś kiedykolwiek pisać == truelub równie dobrze == true == true.
Tom Hawtin - tackline
7
Nie ma powodu do pisania == true. Ale widziałem tyle złego kodu w swojej karierze, że nie pytam już, dlaczego ktoś miałby coś napisać i po prostu akceptuję, że niektórzy napisali niepotrzebny / wątpliwy kod.
R Samuel Klatchko
1
Rzeczywiście, wciąż jest dużo słabego kodu, mimo to, jeśli uznamy go x == trueza zły, ponieważ można go błędnie napisać x = true, nie miałoby sensu zmieniać go na true == xzamiast po prostu x.
Holger,
9

Dotyczy to również ściśle:

if ("foo".equals(bar)) {

co jest wygodne, jeśli nie chcesz zajmować się NPE:

if (bar!=null && bar.equals("foo")) {
cherouvim
źródło
może być wygodne, ale może być niebezpieczne. blue-walrus.com/2010/11/…
Oliver Watkins
@OliverWatkins: Uważam, że ten artykuł jest słaby. Załóżmy, że zmienna była typu int. Jeśli pozostanie niezainicjalizowane, wartość będzie wynosić 0, a obietnica artykułu nie zostanie zachowana. Fakt, że ciągi znaków są obiektami, a wartości int są prymitywami, nie ma znaczenia dla faktu, że programista może zapomnieć o zainicjowaniu zmiennej. W tym pytaniu po prostu rozwiązujemy porównanie zerowych ciągów.
cherouvim
@cherouvim fakt, że intjest to typ prymitywny, jest istotny, ponieważ nie można wywołać equalsna an int, dlatego nie ma sensu dyskutować o tym, x.equals(constant)czy użyć lub constant.equals(x)dla intwartości. W przypadku Integerwartości wartością domyślną jest nullzamiast 0.
Holger
5

Ta sztuczka miała zapobiec v = nullrodzajom literówek.

Ale Java dopuszcza tylko wyrażenia boolowskie jako if()warunki, więc sztuczka nie ma większego sensu, kompilator i tak znajdzie te literówki.

Jest to jednak nadal cenna sztuczka dla kodu C / C ++.

vava
źródło
3

Z tego samego powodu robisz to w C; przypisanie jest wyrażeniem, więc umieszczasz literał po lewej stronie, aby nie można go było nadpisać, jeśli przypadkowo użyjesz =zamiast ==.

Ignacio Vazquez-Abrams
źródło
Jednak w Javie jest to tylko problem dla typów boolowskich, prawda? Każdy inny typ przypisania nie będzie miał typu boolowskiego i powinien powodować błąd kompilatora.
Scott Smith
1
nie, kompilator Java wykryje tego rodzaju literówkę w dowolnej kolejności
vava
@Jijoy - nie sądzę, że ma to coś wspólnego z wydajnością. Brzmi to jak stara sztuczka w C, aby uniknąć przerażającego błędu przypisania w wyrażeniu warunkowym. Pomysł polegał na tym, że jeśli próbujesz sprawdzić, czy xrówna się 5, kiedy błędnie wpiszesz if (x = 5), skompiluje się po cichu (i zawsze oceni do true, niezależnie od wartości x). Jeśli jednak przyzwyczaisz się do określania najpierw literału: „if (5 = x)”, kompilator może dokładnie sprawdzić twoją pracę i zaoszczędzić ci czasu na rozwiązywanie problemów w debugerze. Nowoczesne kompilatory sprawiają, że ten nawyk jest niepotrzebny.
Scott Smith
6
-1: Jeśli przypadkowo użyjesz =, nie skompiluje się w Javie; więc powód z C nie ma zastosowania.
Jon Skeet
Technicznie rzecz biorąc, gdyby objbył typu java.lang.Boolean(duże B), to może mieć znaczenie (myślę, że właściwie nie próbowałem ani nic).
Tom Hawtin - tackline
3

To jest dla osób, które wolą mieć stałą po lewej stronie. W większości przypadków posiadanie stałej po lewej stronie uniemożliwi zgłoszenie wyjątku NullPointerException (lub innego sprawdzania wartości null). Na przykład metoda String równa się również sprawdza wartość null. Posiadanie stałej po lewej stronie uniemożliwi wypisanie dodatkowego czeku. Który w inny sposób jest również wykonywany później. Posiadanie wartości null po lewej stronie jest po prostu spójne.

lubić:

 String b = null;
 "constant".equals(b);  // result to false
 b.equals("constant");  // NullPointerException
 b != null && b.equals("constant");  // result to false
SPee
źródło
to ukrycie NPE po prostu stwarza jeszcze trudniejsze do znalezienia błędów dalej w dół
Oliver Watkins
2

Jest to warunek Yody piszący w inny sposób

W java

String myString = null;
if (myString.equals("foobar")) { /* ... */ } //Will give u null pointer

stan jody

String myString = null;
if ("foobar".equals(myString)) { /* ... */ } // will be false 
Tanmay Naik
źródło
1

Porównaj z następującym kodem:

    String pingResult = "asd";
    long s = System.nanoTime ( );
    if ( null != pingResult )
    {
        System.out.println ( "null != pingResult" );
    }
    long e = System.nanoTime ( );
    System.out.println ( e - s );

    long s1 = System.nanoTime ( );
    if ( pingResult != null )
    {
        System.out.println ( "pingResult != null" );
    }
    long e1 = System.nanoTime ( );
    System.out.println ( e1 - s1 );

Wyjście (po wielu wykonaniach):

null != pingResult
325737
pingResult != null
47027

Dlatego pingResult != nulljest zwycięzcą.

Avinash
źródło
7
Przeprowadź test w dodatkowej pętli! Lub po prostu zamień instrukcje IF. Krótko mówiąc: nie ma różnicy! Pierwsza pętla jest zawsze wolniejsza.
Marcel Jaeschke,
W tym teście pingResult ma zawsze wartość różną od null. Co się dzieje z czasem pingResult jest zerowy? Założę się, że jest niezależny od platformy, ale na moim stosie Oracle Java 1.6 / Linux wyniki są prawie równe (w pętli), z wyjątkiem przypadku, gdy pingResult ma wartość null, oba testy są o kilka procent szybsze.
Ogre Psalm 33
1
jeśli umieścisz blok „pingResult! = null przed null! = blok pingResult”, wynik będzie podobny do „pingResult! = null 325737”
Sola Yang
Jak mówi @Sola Yang, jeśli wcześniej wstawisz pingResult! = Null, zobaczysz, że zajmuje to więcej czasu niż null! = PingResult. Jeśli pingResult! = Null był szybszy niż null! = PingResult IDE (jak IntelliG, Eclipse, ...) wyświetli ostrzeżenie, aby zmusić programistów do użycia tej roli;)
adil.hilmi
1

Ze względu na swoją przemienność jedyna różnica między object == nulla null == object( wersja Yoda ) ma charakter poznawczy : w jaki sposób kod jest czytany i trawiony przez czytelnika. Nie znam jednak ostatecznej odpowiedzi, ale wiem, że osobiście wolę porównywać badany obiekt z czymś innym niż porównywać coś innego z obiektem, który sprawdzam , jeśli to ma jakiś sens. Zacznij od tematu, a następnie wartości, z którą chcesz go porównać.

W niektórych innych językach ten styl porównywania jest bardziej przydatny.

Aby jednak ogólnie ustrzec się przed brakującym znakiem „=”, myślę, że pisanie null == objectjest błędnym działaniem programowania obronnego . Lepszym sposobem obejścia tego konkretnego kodu jest zagwarantowanie zachowania za pomocą testu junit. Pamiętaj, możliwy błąd polegający na pominięciu „=” nie jest zależny od argumentów wejściowych metody - nie jesteś zależny od właściwego użycia tego API przez inne osoby - więc test junit jest idealny, aby się przed tym zabezpieczyć. W każdym razie będziesz chciał napisać testy junit, aby zweryfikować zachowanie; brakujący „=” naturalnie mieści się w zakresie.

Benny Bottema
źródło