Czy „this” może być kiedykolwiek zerowe w Javie?

109

Widziałem tę linię w metodzie klasowej i moją pierwszą reakcją było wyśmiewanie programisty, który ją napisał. Ale potem pomyślałem, że powinienem najpierw upewnić się, że mam rację.

public void dataViewActivated(DataViewEvent e) {
    if (this != null)
        // Do some work
}

Czy ta linia kiedykolwiek zostanie uznana za fałszywą?

Bryce Fischer
źródło
104
Zawsze najpierw wyśmiewaj się, a pytaj później. Łatwiej jest przeprosić, niż odzyskać wspaniałą okazję, by rozerwać kogoś w podmuchu siarki.
Joel Etherton
5
+1 za słowo „podmuch siarki”.
Marty Pitt
13
Wiesz, co jest zabawne? Może się to zdarzyć w C # z powodu błędu kompilatora!
Blindy
1
@Blindy da +1 za próbkę kodu.
Nathan Feger
6
dobrze w C # może mieć wartość null. W niektórych skrajnych przypadkach. Miałem ten sam impuls: wyśmiać frajera, ale potem po prostu się uspokoiłem. Zajrzyj tutaj: stackoverflow.com/questions/2464097/…
Andrei Rînea

Odpowiedzi:

88

Nie, nie może. Jeśli używasz this, to jesteś w instancji, więc thisnie jest null.

JLS mówi:

Używane jako wyrażenie podstawowe słowo kluczowe this oznacza wartość będącą odniesieniem do obiektu, dla którego wywołano metodę instancji (§15.12), lub do konstruowanego obiektu.

Jeśli wywołałeś metodę z obiektu, to obiekt istnieje lub miałbyś NullPointerExceptionprzed (lub jest to metoda statyczna, ale wtedy nie możesz thisw niej użyć ).


Zasoby :

Colin Hebert
źródło
4
Nie wiem dokładnie o Javie, ale w C ++ thismetoda instancji może mieć wartość NULL. Więc nie jestem do końca przekonany, że to wystarczający powód w Javie.
kennytm
4
wyjątek wskaźnika zerowego w czymś podobnym foo.bar()zostałby zgłoszony, gdy foozostanie wykryty null. dzieje się to przed wejściem do metody, ale prawdziwa historia jest taka, że ​​nie ma metody, którą można by spróbować wywołać.
Claudiu
5
@KennyTM: wystarczy w Javie. Jeśli używasz thissłowa kluczowego i kompiluje się, nie jest ono zerowe, gdy je obserwujesz. Ale, jak mówią inni, nie zapobiega to NPE podczas próby wywołania metody, np. Ale to jest całkowicie poza twoją kontrolą jako metoda, a zerowe sprawdzenie w metodzie niczego nie zmieni.
Mark Peters
5
@Kenny: Nie bez niezdefiniowanego zachowania , chociaż jeśli znasz szczegóły swojej implementacji, możesz go użyć.
4
Nie zgadzałbym się na tę ostateczną odpowiedź, nawet jeśli ma to całkowity sens. Mam raporty o awariach dla aplikacji na Androida, w których this == null, gdy metoda instancji jest wywoływana zaraz po tym, jak zmienna jest zerowana z innego wątku. Faktyczne wywołanie przechodzi, nawet jeśli zmienna jest pusta, i ulega awarii, gdy próbuje odczytać członka instancji :)
Adrian Crețu
63

To tak, jakbyś zadał sobie pytanie „Czy żyję?” thisnigdy nie może być zerowe

przechwałek
źródło
44
czy ja żyję ?! o Boże, już nie wiem
Claudiu
Sprawiasz, że brzmi to tak, jakby this != nullbyło oczywiste. Tak nie jest - na przykład w C ++ thismoże tak być NULLdla metody niewirtualnej.
Niki,
1
@nikie W pewnym sensie jest to oczywiste. Nawet w C ++ każdy program, w którym tak się dzieje, ma niezdefiniowane zachowanie. Może się to również zdarzyć w przypadku funkcji wirtualnych w GCC: ideone.com/W7RmU .
Johannes Schaub - litb
8
@John: Jesteś. Część tortur polega na tym, że nie możesz tego potwierdzić.
@ JohannesSchaub-litb To nieprawda, że ​​zachowanie programu w języku C ++ z zerową referencją jest niezdefiniowane. Jest zdefiniowane, o ile tego nie wyłuskujesz
Rune FS
9

Nie, nigdy , słowo kluczowe „this” samo w sobie reprezentuje bieżącą aktywną instancję (obiekt) tej klasy w zakresie tej klasy, za pomocą której można uzyskać dostęp do wszystkich jej pól i składowych (w tym konstruktorów) oraz widocznych z jej klasy nadrzędnej.

Co ciekawsze, spróbuj ustawić to:

this = null;

Pomyśl o tym? Jak to możliwe, czy nie będzie to jak przecinanie gałęzi, na której siedzisz. Ponieważ słowo kluczowe „this” jest dostępne w zakresie klasy, więc gdy tylko powiesz this = null; gdziekolwiek w klasie to po prostu prosisz JVM o zwolnienie pamięci przypisanej do tego obiektu w trakcie jakiejś operacji, na którą JVM po prostu nie może pozwolić, ponieważ musi bezpiecznie wrócić po zakończeniu tej operacji.

Co więcej, próbując this = null; zakończy się błędem kompilatora. Przyczyna jest dość prosta, słowu kluczowemu w Javie (lub jakimkolwiek innym języku) nie można nigdy przypisać wartości, tj. Słowo kluczowe nigdy nie może być lewą wartością operacji przypisania.

Inne przykłady, których nie możesz powiedzieć:

true = new Boolean(true);
true = false;
sactiw
źródło
Dobry przyjaciel wyjaśniający.
Pankaj Sharma
@PankajSharma Thanks :)
sactiw
Znalazłem to, ponieważ chciałem zobaczyć, czy mogę użyć this = null. Moja instancja była w systemie Android, gdzie chciałem usunąć widok i ustawić obiekt obsługujący widok na null. Następnie chciałem użyć metody, remove()która usunęłaby rzeczywisty widok, a następnie obiekt obsługi stałby się bezużyteczny, więc chciałem go wyzerować.
Yokich
Nie jestem pewien, czy zgadzam się z Twoimi roszczeniami. (przynajmniej środkowa część; część l-wartości jest w porządku ... chociaż jestem prawie pewien, że w rzeczywistości istnieją zepsute języki, które pozwalają przypisać np. true = false (a nawet języki, których nie nazwałbym złamaną, mogą na to pozwolić Refleksja lub podobna sztuczka)). W każdym razie, gdybym miał jakiś obiekt foo i miałbym zrobić (ignorując, że jest to nielegalne) foo.this = null, foo nadal wskazywałoby na pamięć, więc JVM nie zbierałby go jako śmieci.
Foon
@Na szczęście pytanie dokładnie mówi o języku Java, ponadto nie spotkałem żadnego języka, w którym ustawienie this = null jest dozwolone. To znaczy, jeśli istnieją takie języki, to bardzo wątpię, że „to” w takich językach będzie miało ten sam kontekst i ideologię.
sactiw
7

Jeśli kompilujesz z -target 1.3lub wcześniej, może to być plik zewnętrzny . A przynajmniej kiedyś ...thisnull

Tom Hawtin - haczyk
źródło
2
Myślę, że poprzez refleksję możemy ustawić to zewnętrzne na zero. może być primaaprilisowym żartem z kogoś, kto dostał wyjątek zerowego wskaźnika w odwoływaniu sięOuter.this.member
niepodważalny
3

Nie. Aby wywołać metodę instancji klasy, instancja musi istnieć. Wystąpienie jest niejawnie przekazywane jako parametr do metody, do której odwołuje się this. Gdyby tak thisbyło, nullnie byłoby instancji do wywołania metody.

Claudiu
źródło
3

Nie wystarczy, że język to wymusza. Maszyna wirtualna musi to wymusić. O ile maszyna wirtualna tego nie wymusza, można napisać kompilator, który nie wymusza sprawdzenia wartości null przed wywołaniem metody napisanej w języku Java. Kody operacyjne dla wywołania metody instancji obejmują ładowanie tego odniesienia na stos, patrz: http://java.sun.com/docs/books/jvms/second_edition/html/Compiling.doc.html#14787 . Zastąpienie tego pustym odniesieniem rzeczywiście spowodowałoby, że test byłby fałszywy

Rune FS
źródło
3

W statycznych metodach klas thisnie jest zdefiniowany, ponieważ thisjest powiązany z instancjami, a nie z klasami. Uważam, że próba użycia thissłowa kluczowego w kontekście statycznym spowodowałaby błąd kompilatora .

burkestar
źródło
2

Normalny thisnigdy nie może być nullw prawdziwym kodzie Java 1 , a Twój przykład używa normalnego this. Zobacz inne odpowiedzi, aby uzyskać więcej informacji.

Kwalifikowany nigdy nie this powinien być null, ale można to przełamać. Rozważ następujące:

public class Outer {
   public Outer() {}

   public class Inner {
       public Inner() {}

       public String toString() {
           return "outer is " + Outer.this;  // Qualified this!!
       }
   }
}

Kiedy chcemy utworzyć instancję Inner, musimy to zrobić:

public static void main(String[] args) {
    Outer outer = new Outer();
    Inner inner = outer.new Inner();
    System.out.println(inner);

    outer = null;
    inner = outer.new Inner();  // FAIL ... throws an NPE
}

Wynik to:

outer is Outer@2a139a55
Exception in thread "main" java.lang.NullPointerException
        at Outer.main(Outer.java:19)

pokazując, że nasza próba stworzenia Innerz nullodniesieniem do ich Outerzawiódł.

W rzeczywistości, jeśli trzymasz się obwiedni „czystej Javy”, nie możesz tego złamać.

Jednak każda Innerinstancja ma ukryte finalpole syntetyczne (nazywane "this$0"), które zawiera odwołanie doOuter . Jeśli jesteś naprawdę podstępny, możesz użyć "nieczystych" środków, aby przypisać nulldo pola.

  • Możesz to Unsafezrobić.
  • Możesz do tego użyć kodu natywnego (np. JNI).
  • Możesz to zrobić za pomocą refleksji.

Tak czy inaczej, efekt końcowy jest taki, że Outer.thiswyrażenie wyniesie null2 .

W skrócie, jest to możliwe dla wykwalifikowanego thisbyć null. Ale jest to niemożliwe, jeśli Twój program przestrzega zasad „czystej Java”.


1 - Dyskontuję sztuczki, takie jak „pisanie” kodów bajtowych ręcznie i przekazywanie ich jako prawdziwej Javy, poprawianie kodów bajtowych za pomocą BCEL lub podobnego, lub przeskakiwanie do kodu natywnego i manipulowanie zapisanymi rejestrami. IMO, czyli NIE JAVA. Hipotetycznie, takie rzeczy mogą się również zdarzyć w wyniku błędu JVM ... ale nie przypominam sobie, żeby każdy widział raporty o błędach.

2 - W rzeczywistości JLS nie mówi, jakie będzie zachowanie, i może być zależne od implementacji ... między innymi.

Stephen C.
źródło
1

Gdy wywołasz metodę w nullodwołaniu, NullPointerExceptionzostanie ona wyrzucona z maszyny wirtualnej Java. Dzieje się tak według specyfikacji, więc jeśli maszyna wirtualna Java jest ściśle zgodna ze specyfikacją, thisnigdy by tak nie było null.

tia
źródło
1

Jeśli metoda jest statyczna, nie ma żadnej this. Jeśli metoda jest wirtualna, thisnie może mieć wartości null, ponieważ w celu wywołania metody środowisko wykonawcze będzie musiało odwołać się do tabeli vtable za pomocą thiswskaźnika. Jeśli metoda nie jest wirtualna , to tak, możliwe, że thisjest zerowa.

C # i C ++ zezwalają na metody niewirtualne, ale w Javie wszystkie metody niestatyczne są wirtualne, więc thisnigdy nie będą miały wartości NULL.

John Henckel
źródło
0

tl; dr, "this" można wywołać tylko z metody niestatycznej i wszyscy wiemy, że metoda niestatyczna jest wywoływana z jakiegoś obiektu, który nie może mieć wartości NULL.

Adam
źródło