2010-02-02 13 views

Odpowiedz

23

Zmienne instancji i klasy są inicjowane na wartość null (lub 0), ale zmienne lokalne nie są.

Zobacz §4.12.5 z JLS na bardzo szczegółowym wyjaśnieniem który mówi w zasadzie to samo:

Every variable in a program must have a value before its value is used:

  • Each class variable, instance variable, or array component is initialized with a default value when it is created:
    • [snipped out list of all default values]
  • Each method parameter is initialized to the corresponding argument value provided by the invoker of the method.
  • Each constructor parameter is initialized to the corresponding argument value provided by a class instance creation expression or explicit constructor invocation.
  • An exception-handler parameter is initialized to the thrown object representing the exception.
  • A local variable must be explicitly given a value before it is used, by either initialization or assignment, in a way that can be verified by the compiler using the rules for definite assignment.
+1

Dlaczego została podjęta ta decyzja? Dlaczego po prostu nie zainicjować ich na 'null' jak zmiennych klasowych? –

+0

Być może dla lepszego kodu/czytelności? –

+0

Istnieje wiele pytań dotyczących stackoverflow dotyczących tego, dlaczego zmienne lokalne nie mają wartości domyślnych. na przykład http://stackoverflow.com/questions/415687/why-are-local-variables-not-initialized-in-java –

7

To dlatego, że Java jest bardzo pomocny (w miarę możliwości).

Użyje tej samej logiki, aby złapać kilka bardzo interesujących skrzynek krawędzi, które mogłeś przegapić. Na przykład:

int x; 

if(cond2) 
    x=2; 
else if(cond3) 
    x=3; 

System.out.println("X was:"+x); 

To się nie powiedzie, ponieważ był inny przypadek, który nie został określony. Faktem jest, że przypadek else nie powinien tu być absolutnie określony, nawet jeśli jest to po prostu błąd (to samo dotyczy domyślnego warunku w instrukcji switch).

Co należy ci zabrać, co ciekawe, nie zainicjuj zmiennych lokalnych, dopóki nie zorientujesz się, że tak naprawdę musisz to zrobić. Jeśli masz zwyczaj powtarzania "int x = 0;" zapobiegniesz funkcjonowaniu tego fantastycznego detektora "złej logiki". Ten błąd zaoszczędził mi więcej czasu niż raz.

4

Ditto na Bill K. dodam:

Kompilator Java może chronić przed zranieniem siebie poprzez brak ustawić zmienną przed użyciem go w obrębie funkcji. Zatem wyraźnie NIE ustawia wartości domyślnej, jak opisuje Bill K.

Ale jeśli chodzi o zmienne klasowe, kompilatorowi byłoby bardzo trudno to zrobić. Zmienna klasy może być ustawiona przez dowolną funkcję w klasie. Kompilatorowi trudno byłoby określić wszystkie możliwe zlecenia, w których można wywoływać funkcje. Przynajmniej będzie musiał przeanalizować wszystkie klasy w systemie, które wywołują jakąkolwiek funkcję w tej klasie. Być może trzeba będzie zbadać zawartość dowolnych plików danych lub bazy danych i jakoś przewidzieć, jakie dane wejściowe wprowadzą użytkownicy. W najlepszym wypadku zadanie byłoby niezwykle złożone, w najgorszym przypadku niemożliwe. Tak więc w przypadku zmiennych klas warto zapewnić niezawodne ustawienie domyślne. Ta wartość domyślna polega w zasadzie na wypełnianiu pola bitami równymi zero, więc otrzymujesz wartość zerową dla odniesień, zero dla liczb całkowitych, wartość false dla wartości logicznych itd.

Zgodnie z tym, co mówi Bill, zdecydowanie nie powinno ci się stać automatycznie inicjowanie zmiennych po ich zadeklarowaniu. Inicjuj zmienne tylko w momencie deklaracji, jeśli ma to sens w kontekście Twojego programu. Na przykład, jeśli w 99% przypadków x ma wynosić 42, ale w pewnym stanie IF można odkryć, że jest to przypadek specjalny, a x powinno wynosić 666, to w porządku, zacząć od "int x = 42;" a wewnątrz IF nadpisuje to. Ale w bardziej normalnym przypadku, gdy obliczasz wartość w oparciu o dowolne warunki, nie zainicjuj na dowolną liczbę. Po prostu wypełnij obliczoną wartość.Następnie, jeśli popełnisz błąd logiczny i nie ustawisz wartości pod pewną kombinacją warunków, kompilator może powiedzieć ci, że zgniłeś, a nie użytkownika.

PS Widziałem wiele lame programów, które mówią rzeczy jak:

HashMap myMap=new HashMap(); 
myMap=getBunchOfData(); 

Dlaczego utworzyć obiekt zainicjować zmienną, gdy wiesz, że natychmiast rzuci ten obiekt oddalony o milisekundę później? To tylko strata czasu.

Edit

Aby wziąć trywialny przykład, załóżmy, że napisał to:

int foo; 
if (bar<0) 
    foo=1; 
else if (bar>0) 
    foo=2; 
processSomething(foo); 

To wygeneruje błąd w czasie kompilacji, ponieważ kompilator nie zauważy, że gdy pasek == 0, nigdy nie ustawiłeś foo, ale potem próbujesz go użyć.

Ale jeśli zainicjować foo do wartości manekina, jak

int foo=0; 
if (bar<0) 
    foo=1; 
else if (bar>0) 
    foo=2; 
processSomething(foo); 

Następnie kompilator będzie zobaczyć, że bez względu na to, jaka jest wartość baru, foo zostanie ustawiony na czymś, więc nie będzie produkować błąd. Jeśli naprawdę chcesz, aby foo miało wartość 0, gdy bar wynosi 0, to jest w porządku. Ale jeśli to, co się naprawdę wydarzyło, oznacza, że ​​jednym z testów było < = lub> = lub zamierzałeś uwzględnić finał innego, gdy bar == 0, to oszukałeś kompilator, który nie wykrył twojego błędu. A tak na marginesie, tak myślę, że taka konstrukcja jest kiepskim stylem kodowania: kompilator nie tylko nie ma pewności, co zamierzałeś, ale też nie może być przyszłym programistą konserwacji.

+0

Dlaczego po prostu nie zainicjować go na wartość null? Jedyny problematyczny problem, jaki mogę sobie wyobrazić, to zadeklarowanie ostatniej zmiennej, która musi być ustawiona na wartość w bloku try. Ale możesz po prostu zdefiniować tutaj zmienną tymczasową i przypisać jej wartość po bloku try do ostatniej. – sibidiba

+0

@sibidiba: Na jaką część odpowiedzi odpowiadasz? Jeśli masz na myśli ogólnie, stracisz przewagę sprawdzania podczas kompilacji. Zobacz odpowiedź Billa K. Jeśli masz na myśli mój komentarz końcowy z przykładem myMap, to prostym rozwiązaniem jest napisanie "HashMap myMap = getBunchOfData()" i pomiń bezcelowy dodatkowy obiekt. – Jay

1

Podobają mi się punkty Billa K, że pozwolę kompilatorowi pracować dla ciebie - zainicjowałem każdą automatyczną zmienną, ponieważ "wydawało mi się, że to jest Java". Nie mogłem zrozumieć, że zmienne klasy (tj. Trwałe rzeczy, o które obawiają się konstruktorzy) i zmienne automatyczne (jakiś licznik itp.) Są różne, mimo że WSZYSTKO jest klasą w Javie.

Wróciłem więc i usunął inicjalizacji byłbym za pomocą, na przykład

List <Thing> somethings = new List<Thing>(); 
somethings.add(somethingElse); // <--- this is completely unnecessary 

Nicea. Dostawałem ostrzeżenie o kompilacji dla

List<Thing> somethings = new List(); 

i myślałem, że problemem był brak inicjalizacji. ŹLE. Problem polegał na tym, że nie zrozumiałem reguł i potrzebowałem <Thing> zidentyfikowanego w "nowym", a nie rzeczywistych elementów typu <Thing> utworzonych.

(Next muszę nauczyć się umieścić dosłowny mniej niż i większe niż znaki w HTML!)

+0

@Bill IV Oczyściłem cytaty dla ciebie; wszystko, co musisz zrobić, to oznaczenie sekcji kodu jako kodu (ikona 101/010 nad edytorem to zrobi). –

+0

Dziękuję, Carl! –

0

nie wiem logikę, ale zmienne lokalne nie są inicjowane null. Myślę, że twoje życie jest łatwe. Mogliby to zrobić za pomocą zmiennych klas, gdyby było to możliwe. Nie oznacza to, że musisz go zainicjować na początku. To jest w porządku:

MyClass cls; 
if (condition) { 
    cls = something; 
else 
    cls = something_else; 
0

Oczywiście, jeśli już naprawdę mam dwie linie na górze siebie jako show-go zadeklarować, wypełnić go, nie ma potrzeby domyślnego konstruktora. Ale na przykład, jeśli chcesz zadeklarować coś raz i użyć go kilka lub wiele razy, domyślny konstruktor lub deklaracja zerowa jest istotna.Czy wskaźnik do obiektu jest tak lekki, że lepiej jest go przydzielić w pętli, ponieważ alokacja wskaźnika jest o wiele mniejsza niż instancjonowanie obiektu? (Przypuszczalnie istnieje ważny powód dla nowego obiektu na każdym etapie pętli).

Rachunek IV

+0

Nie mówię, nie twórz wartości i używaj jej ponownie. Mówię: unikaj wypełniania fałszywej wartości jako symbolu zastępczego, dopóki nie znajdziesz prawdziwej wartości. Jeśli po prostu zadeklarujesz zmienną, ale pozostawisz ją niewypełnioną, to później przejdziesz przez wszystkie twoje IF i BUT, aby ją wypełnić, wtedy spróbujesz jej użyć, kompilator powie Ci, czy istnieje jakaś możliwa ścieżka przez twoje miejsce, w którym nigdy go nie wypełniłeś . Ale jeśli wypełnisz fałszywą wartość, kompilator nie będzie mógł wiedzieć, że jest to fikcyjna wartość, a nie prawdziwa wartość, a więc tracisz przewagę sprawdzania podczas kompilacji. – Jay

Powiązane problemy