Na etapie programowania istnieją pewne zmienne, które muszą być ustalone w tym samym przebiegu, ale z czasem mogą wymagać modyfikacji. Na przykład, boolean
aby zasygnalizować tryb debugowania, więc robimy rzeczy w programie, których normalnie byśmy nie zrobili.
Czy źle jest przechowywać te wartości w stałej, tj. final static int CONSTANT = 0
W Javie? Wiem, że stała pozostaje taka sama w czasie wykonywania, ale czy powinna być taka sama podczas całego rozwoju, z wyjątkiem oczywiście nieplanowanych zmian?
Szukałem podobnych pytań, ale nie znalazłem niczego, co pasowałoby dokładnie do mojego.
final
daje wymuszoną przez kompilator gwarancję, że program nie zmodyfikuje wartości. Nie zrezygnowałbym z tego tylko dlatego, że programista może chcieć zmodyfikować wartość przypisaną w kodzie źródłowym.gravity
gry / biegu. Niekoniecznie oznaczają, żegravity
jest taki sam na każdej planecie ... To powiedziawszy, zdrowym rozwiązaniem jest utworzeniegravity
stałej, ale pobranie jej zplanet
pliku lub bazy danych na początku odpowiedniego zakresu.Odpowiedzi:
W Javie statyczne stałe końcowe mogą być kopiowane przez kompilator jako ich wartości do kodu, który ich używa . W wyniku tego, jeśli zwolnisz nową wersję kodu i istnieje pewna zależność niższego szczebla, która wykorzystała stałą, stała w tym kodzie nie zostanie zaktualizowana, chyba że kod dalszego przesyłania zostanie ponownie skompilowany. Może to stanowić problem, jeśli następnie wykorzystają tę stałą z kodem, który oczekuje nowej wartości, ponieważ nawet jeśli kod źródłowy jest poprawny, kod binarny nie jest.
Jest to brodawka w projektowaniu Java, ponieważ jest to jeden z niewielu przypadków (być może jedyny przypadek), w którym zgodność źródła i zgodność binarna nie są takie same. Z wyjątkiem tego przypadku można zamienić zależność na nową wersję zgodną z interfejsem API bez konieczności ponownej kompilacji przez użytkowników zależności. Jest to oczywiście niezwykle ważne, biorąc pod uwagę ogólny sposób zarządzania zależnościami Java.
Co gorsza, kod po prostu dyskretnie zrobi coś złego, zamiast generować przydatne błędy. Gdyby zastąpić zależność wersją o niekompatybilnych definicjach klas lub metod, otrzymalibyśmy błędy modułu ładującego lub wywołania, które przynajmniej dają dobre wskazówki co do tego, na czym polega problem. O ile nie zmieniłeś rodzaju wartości, ten problem będzie wyglądał jak tajemnicze złe zachowanie w czasie wykonywania.
Bardziej denerwujące jest to, że dzisiejsze maszyny JVM mogą łatwo wstawiać wszystkie stałe w środowisku wykonawczym bez obniżania wydajności (poza koniecznością ładowania klasy definiującej stałą, która i tak prawdopodobnie jest ładowana), niestety semantyka języka pochodzi z dni przed JIT . I nie mogą zmienić języka, ponieważ kod skompilowany z poprzednimi kompilatorami nie będzie poprawny. Kompatybilność z błędami uderza ponownie.
Z tego powodu niektórzy radzą, aby w ogóle nigdy nie zmieniać statycznej wartości końcowej. W przypadku bibliotek, które mogą być szeroko rozpowszechniane i aktualizowane w nieznany sposób w nieznanych czasach, jest to dobra praktyka.
W swoim własnym kodzie, szczególnie na szczycie hierarchii zależności, zapewne ci się uda. Ale w takich przypadkach zastanów się, czy naprawdę potrzebujesz stałej, aby być publicznym (lub chronionym). Jeśli stałą jest tylko widoczność pakietu, uzasadnione jest, w zależności od okoliczności i standardów kodu, że cały pakiet zawsze zostanie ponownie skompilowany na raz, a problem zniknie. Jeśli stała jest prywatna, nie masz problemu i możesz ją zmienić w dowolnym momencie.
źródło
Wszystko w kodzie źródłowym, w tym
const
zadeklarowane stałe globalne, może ulec zmianie wraz z nową wersją oprogramowania.Słowa kluczowe
const
(lubfinal
w Javie) służą do sygnalizowania kompilatorowi, że ta zmienna nie zmieni się podczas działania tego wystąpienia programu . Nic więcej. Jeśli chcesz wysyłać wiadomości do następnego opiekuna, użyj komentarza w źródle, po to są.To zdecydowanie lepszy sposób komunikowania się z przyszłym sobą.
źródło
TaxRate
byciepublic
denerwuje mnie. Chciałbym mieć pewność, że zmiana ta dotyczy tylko działu sprzedaży, a nie naszych dostawców, którzy obciążają nas podatkiem. Kto wie, co wydarzyło się w bazie kodu od czasu napisania tego komentarza.public const
pola powinny być używane tylko dla rzeczy, które nigdy się nie zmienią, takich jak Math.pi. Jeśli tworzysz bibliotekę, rzeczy, które mogą się zmienić podczas programowania lub nowej wersji, powinny byćpublic static readonly
takie, aby nie powodować problemów z użytkownikami Twojej biblioteki.Musimy rozróżnić dwa aspekty stałych:
A potem jest pokrewny trzeci rodzaj: zmienne, których wartość się nie zmienia, tj. Nazwy wartości. Różnica między tymi niezmiennymi zmiennymi a stałą polega na tym, że wartość jest określana / przypisywana / inicjalizowana: zmienna jest inicjalizowana w czasie wykonywania, ale wartość stałej jest znana podczas programowania. To rozróżnienie jest nieco mętne, ponieważ wartość może być znana podczas programowania, ale w rzeczywistości jest tworzona tylko podczas inicjalizacji.
Ale jeśli wartość stałej jest znana w czasie kompilacji, to kompilator może wykonywać obliczenia z tą wartością. Na przykład język Java ma pojęcie wyrażeń stałych . Stałe wyrażenie to dowolne wyrażenie, które składa się tylko z literałów pierwotnych lub łańcuchów, operacji na stałych wyrażeniach (takich jak rzutowanie, dodawanie, łączenie łańcuchów) i zmiennych stałych. [ JLS § 15.28 ] Zmienna stała jest
final
zmienną, która jest inicjowana stałym wyrażeniem. [JLS §4.12.4] Tak więc dla Javy jest to stała czasowa kompilacji:Staje się to interesujące, gdy zmienna stała jest używana w wielu jednostkach kompilacji, a następnie deklaracja jest zmieniana. Rozważać:
A.java
:B.java
:Teraz, kiedy kompilujemy te pliki,
B.class
kod bajtowy zadeklaruje pole,Y = 9
ponieważB.Y
jest zmienną stałą.Ale kiedy zmienimy
A.X
zmienną na inną wartość (powiedzmyX = 0
) i ponownie skompilujemy tylkoA.java
plik, wówczasB.Y
nadal będzie się odnosić do starej wartości. Ten stanA.X = 0, B.Y = 9
jest niezgodny z deklaracjami w kodzie źródłowym. Miłego debugowania!Nie oznacza to, że stałe nigdy nie powinny być zmieniane. Stałe są zdecydowanie lepsze niż magiczne liczby, które pojawiają się bez wyjaśnienia w kodzie źródłowym. Jednak wartość stałych publicznych jest częścią publicznych API . Nie jest to specyficzne dla Javy, ale występuje również w C ++ i innych językach, które zawierają osobne jednostki kompilacji. Jeśli zmienisz te wartości, będziesz musiał ponownie skompilować cały zależny kod, tj. Wykonać czystą kompilację.
W zależności od charakteru stałych mogły one prowadzić do błędnych założeń programistów. Jeśli te wartości zostaną zmienione, mogą spowodować błąd. Na przykład można wybrać zestaw stałych, aby tworzyły pewne wzorce bitowe, np
public static final int R = 4, W = 2, X = 1
. Jeśli zostaną one zmienione w celu utworzenia innej struktury, takiej jakR = 0, W = 1, X = 2
istniejący kod, taki jak, stanieboolean canRead = perms & R
się niepoprawny. Pomyśl tylko o zabawie, któraInteger.MAX_VALUE
się zmieni! Nie ma tutaj poprawki, należy tylko pamiętać, że wartość niektórych stałych jest naprawdę ważna i nie można jej po prostu zmienić.Ale dla większości stałych zmiana ich będzie w porządku, o ile uwzględnione zostaną powyższe ograniczenia. Stałą można bezpiecznie zmienić, gdy znaczenie, a nie konkretna wartość, jest ważne. Dzieje się tak np. W przypadku tunerów takich jak
BORDER_WIDTH = 2
lubTIMEOUT = 60; // seconds
lub szablonów takich jakAPI_ENDPOINT = "https://api.example.com/v2/"
- choć prawdopodobnie niektóre lub wszystkie z nich powinny być określone w plikach konfiguracyjnych, a nie w kodzie.źródło
Stała gwarantuje się tylko jako stała przez cały czas działania środowiska wykonawczego aplikacji . Tak długo, jak to prawda, nie ma powodu, aby nie korzystać z funkcji języka. Musisz tylko wiedzieć, jakie są konsekwencje użycia flagi stała vs. kompilator w tym samym celu:
Po utrzymaniu aplikacji, która włącza rysowanie obwiedni dla kształtów, abyśmy mogli debugować sposób ich rysowania, napotkaliśmy problem. Po refaktoryzacji cały kod wyłączony przez flagi kompilatora nie zostałby skompilowany. Następnie celowo zmieniliśmy nasze flagi kompilatora na stałe aplikacji.
Mówię to, aby wykazać, że istnieją kompromisy. Waga kilku booleanów nie spowodowała, że w aplikacji zabraknie pamięci lub nie zajmie zbyt wiele miejsca. Może to nie być prawdą, jeśli twoja stała jest naprawdę dużym obiektem, który zasadniczo ma uchwyt do wszystkiego w twoim kodzie. Jeśli nie zajmie się usunięciem wszystkich odniesień do obiektu, gdy nie jest już potrzebny, obiekt może być źródłem wycieku pamięci.
Nie jestem fanem prostych oświadczeń ogólnych , ale ogólnie twój starszy kolega ma rację. Jeśli coś musi się często zmieniać, może to wymagać elementu konfigurowalnego. Na przykład możesz prawdopodobnie przekonać swojego kolegę do stałej,
IsInDebugMode = true
gdy chcesz zabezpieczyć jakiś kod przed złamaniem. Jednak niektóre rzeczy mogą wymagać zmiany częściej niż wydanie aplikacji. W takim przypadku potrzebny jest sposób zmiany tej wartości w odpowiednim czasie. Możesz wziąć przykładTaxRate = .065
. Może to być prawdą w momencie kompilacji kodu, ale z powodu nowych przepisów może on ulec zmianie przed wydaniem kolejnej wersji aplikacji. To jest coś, co wymaga aktualizacji albo z jakiegoś mechanizmu przechowywania (jak plik lub baza danych)źródło
#ifdef
s? Ponieważ są one oparte na tekstowym podstawieniu kodu źródłowego, nie są częścią semantyki języka programowania. Pamiętaj, że Java nie ma preprocesora.#ifdef
flagi. Chociaż nie są one częścią semantyki języka C, są częścią języka C #. Pisałem dla szerszego kontekstu agnostycyzmu językowego.const
,#define
Czyfinal
jest to wskazówka kompilator (uwaga, że#define
w rzeczywistości nie jest wskazówką, jego makro i znacznie bardziej wydajne). Wskazuje, że wartość nie zmieni się podczas wykonywania programu i można dokonać różnych optymalizacji.Jednak, jako wskazówka kompilatora, kompilator robi rzeczy, których programista nie zawsze może oczekiwać. W szczególności, javac wstawi a,
static final int FOO = 42;
tak że gdziekolwiekFOO
zostanie użyty, odczytany zostanie rzeczywisty skompilowany kod bajtowy42
.Nie jest to zbyt dużym zaskoczeniem, dopóki ktoś nie zmieni wartości bez ponownej kompilacji drugiej jednostki kompilacji (pliku .java) - i
42
resztek w kodzie bajtów ( czy możliwe jest wyłączenie wstawiania statycznych zmiennych końcowych przez javac? ).Robienie czegoś
static final
oznacza, że tak właśnie będzie i na zawsze będzie to więcej, a zmiana tego jest naprawdę wielką sprawą - zwłaszcza jeśli coś takiego nie jestprivate
.Stałe dla rzeczy takich jak
final static int ZERO = 0
nie jest problemem.final static double TAX_RATE = 0.55
(poza tym, że pieniądze i podwójne są złe i powinny używać BigDecimal, ale wtedy nie są prymitywne, a zatem nie uwzględniane) jest problemem i należy je dokładnie zbadać, gdzie są używane.źródło
is a problem and should be examined with great care for where it is used.
Dlaczego to jest problem?Jak sama nazwa wskazuje, stałe nie powinny się zmieniać w czasie działania i moim zdaniem stałe są zdefiniowane, aby nie ulegały zmianie przez dłuższy czas (możesz spojrzeć na to pytanie SO, aby uzyskać więcej informacji.
Jeśli chodzi o potrzebę użycia flag (np. W trybie programowania), należy zamiast tego użyć pliku konfiguracyjnego lub parametru startowego (wiele IDE obsługuje konfigurowanie parametru startowego dla poszczególnych projektów; zapoznaj się z odpowiednią dokumentacją), aby włączyć ten tryb - w ten sposób zachowujesz elastyczność korzystania z takiego trybu i nie możesz zapomnieć o zmianie go za każdym razem, gdy kod produktywny.
źródło
Możliwość zmiany między uruchomieniami jest jednym z najważniejszych punktów definiowania stałej w kodzie źródłowym!
Stała zapewnia dobrze zdefiniowaną i udokumentowaną (w pewnym sensie) lokalizację do zmiany wartości w dowolnym momencie przez cały czas istnienia kodu źródłowego. Jest to również obietnica, że zmiana stałej w tym miejscu faktycznie zmieni wszystkie wystąpienia tego, co oznacza.
Jako negatywny przykład: nie ma sensu mieć stałej,
TRUE
która ewaluujetrue
w języku, który faktycznie matrue
słowo kluczowe. Nigdy, nigdy, ani razu nie zadeklarujesz,TRUE=false
chyba że okrutny żart.Oczywiście istnieją inne zastosowania stałych, na przykład skracanie kodu (
CO_NAME = 'My Great World Unique ACME Company'
), unikanie powielania (PI=3.141
), ustawianie konwencji (TRUE=1
) lub cokolwiek innego, ale posiadanie określonej pozycji do zmiany stałej jest z pewnością jednym z najważniejszych.źródło