Nie rozumiem, gdzie final
słowo kluczowe jest naprawdę przydatne, gdy jest używane w parametrach metody.
Jeśli wykluczymy korzystanie z anonimowych klas, czytelność i zamierzone deklaracje, wydaje mi się to prawie bezwartościowe.
Wymuszanie, że niektóre dane pozostają stałe, nie jest tak silne, jak się wydaje.
Jeśli parametr jest prymitywny, nie będzie miał wpływu, ponieważ parametr jest przekazywany do metody jako wartość, a zmiana go nie będzie miała wpływu poza zakres.
Jeśli przekazujemy parametr przez referencję, wówczas sama referencja jest zmienną lokalną, a jeśli referencja zostanie zmieniona w ramach metody, nie miałoby to żadnego efektu spoza zakresu metody.
Rozważ prosty przykładowy test poniżej. Ten test jest pozytywny, chociaż metoda zmieniła wartość podanego mu odniesienia, nie ma to jednak żadnego efektu.
public void testNullify() {
Collection<Integer> c = new ArrayList<Integer>();
nullify(c);
assertNotNull(c);
final Collection<Integer> c1 = c;
assertTrue(c1.equals(c));
change(c);
assertTrue(c1.equals(c));
}
private void change(Collection<Integer> c) {
c = new ArrayList<Integer>();
}
public void nullify(Collection<?> t) {
t = null;
}
źródło
int foo(int* bar)
i ostatniint foo(int* &bar)
. Drugi przekazuje wskaźnik przez referencję, pierwszy przekazuje referencję według wartości.Odpowiedzi:
Zatrzymaj zmianę przypisania zmiennej
Chociaż te odpowiedzi są interesujące intelektualnie, nie przeczytałem krótkiej prostej odpowiedzi:
Niezależnie od tego, czy zmienna jest zmienną statyczną, zmienną składową, zmienną lokalną lub zmienną argument / parametr, efekt jest całkowicie taki sam.
Przykład
Zobaczmy efekt w akcji.
Rozważ tę prostą metodę, w której obydwu zmiennym ( arg i x ) można przypisać różne obiekty.
Oznacz zmienną lokalną jako końcową . Powoduje to błąd kompilatora.
Zamiast tego oznaczmy zmienną parametru jako końcową . To również powoduje błąd kompilatora.
Morał historii:
Nigdy nie przypisuj ponownie argumentów
Jako dobrą praktykę programowania (w dowolnym języku), nigdy nie należy ponownie przypisywać zmiennej parametr / argument do obiektu innego niż obiekt przekazany przez metodę wywołującą. W powyższych przykładach nigdy nie należy pisać wiersza
arg =
. Ponieważ ludzie popełniają błędy, a programiści są ludźmi, poprośmy kompilatora o pomoc. Oznacz każdą zmienną parametru / argumentu jako „końcową”, aby kompilator mógł znaleźć i oflagować takie ponowne przypisania.Z perspektywy czasu
Jak zauważono w innych odpowiedziach… Biorąc pod uwagę pierwotny cel projektowy Javy polegający na pomocy programistom w unikaniu głupich błędów, takich jak czytanie poza końcem tablicy, Java powinna była zostać zaprojektowana w taki sposób, aby automatycznie wymuszała wszystkie zmienne parametrów / argumentów jako „ostateczne”. Innymi słowy, argumenty nie powinny być zmiennymi . Ale z perspektywy czasu to wizja 20/20, a projektanci Java mieli wtedy pełne ręce roboty.
Więc zawsze dodawaj
final
do wszystkich argumentów?Czy powinniśmy dodawać
final
do każdego deklarowanego parametru metody?➥ Dodawaj
final
tylko wtedy, gdy kod metody jest długi lub skomplikowany, gdzie argument może zostać pomylony ze zmienną lokalną lub zmienną składową i ewentualnie ponownie przypisany.Jeśli przejdziesz do praktyki, by nigdy nie przypisywać argumentu, będziesz skłonny dodać
final
do każdego z nich. Jest to jednak uciążliwe i sprawia, że deklaracja jest nieco trudniejsza do odczytania.W przypadku krótkiego prostego kodu, w którym argument jest oczywiście argumentem, a nie zmienną lokalną ani zmienną składową, nie zawracam sobie głowy dodaniem
final
. Jeśli kod jest dość oczywisty, bez szansy, że ja lub inny programista przeprowadzę konserwację lub refaktoryzację przypadkowo pomylę zmienną argumentu jako coś innego niż argument, nie przejmuj się. W mojej własnej pracy dodajęfinal
tylko dłuższy lub bardziej zaangażowany kod, w którym argument mógłby pomylić się ze zmienną lokalną lub składową.Dodano kolejną skrzynkę dla kompletności
źródło
message = ( msg || 'Hello World"' )
. Po prostu nie ma powodu, aby nie używać osobnego var. Jedyny koszt to kilka bajtów pamięci.message = ( msg || 'Hello World"' )
ryzykuje mnie później przypadkowomsg
. Kiedy moim zamiarem jest „zachowanie bez argumentu / zerowy / niezdefiniowany argument jest nie do odróżnienia od przekazania"Hello World"
”, dobrą praktyką programistyczną jest zobowiązanie się do niego na początku funkcji. [Można to osiągnąć bez zmiany przypisania, zaczynając od,if (!msg) return myfunc("Hello World");
ale staje się nieporęczne z wieloma argumentami.] W rzadkich przypadkach, gdy logika funkcji powinna dbać o to, czy użyto wartości domyślnej, wolałbym wyznaczyć specjalną wartość wartownika (najlepiej publiczną) .message
imsg
. Ale jeśli nazwałby to czymś takimprocessedMsg
lub czymś, co zapewnia dodatkowy kontekst - prawdopodobieństwo pomyłki jest znacznie mniejsze. Skoncentruj się na tym, co mówi, a nie na tym, jak to mówi. ;)Czasami miło jest wyrazić (dla czytelności), że zmienna się nie zmienia. Oto prosty przykład, w którym używanie
final
może zaoszczędzić kilka potencjalnych problemów:Jeśli zapomnisz słowo kluczowe „to” na seter, zmienna, którą chcesz ustawić, nie zostanie ustawiona. Jeśli jednak użyjesz
final
słowa kluczowego w parametrze, błąd zostanie przechwycony podczas kompilacji.źródło
Tak, z wyłączeniem anonimowych klas, czytelności i deklaracji zamiaru, jest to prawie bezwartościowe. Czy te trzy rzeczy są jednak bezwartościowe?
Osobiście nie używam
final
zmiennych lokalnych i parametrów, chyba że używam zmiennej w anonimowej klasie wewnętrznej, ale z pewnością widzę sens tych, którzy chcą wyjaśnić, że sama wartość parametru się nie zmieni (nawet jeśli obiekt, do którego się odnosi, zmienia zawartość). Uważam, że dla tych, którzy zwiększają czytelność, jest to całkowicie rozsądne.Punkt byłoby ważniejsze, czy ktoś rzeczywiście twierdząc, że nie utrzymać stałą danych w taki sposób, że nie robi - ale nie mogę sobie przypomnieć widząc żadnych takich roszczeń. Czy sugerujesz, że istnieje znaczna liczba programistów, którzy sugerują, że
final
ma większy efekt niż w rzeczywistości?EDYCJA: Naprawdę powinienem to wszystko podsumować odniesieniem do Monty Pythona; pytanie wydaje się nieco podobne do pytania „Co Rzymianie kiedykolwiek dla nas zrobili?”
źródło
Pozwól mi wyjaśnić co nieco o jednym przypadku, gdzie trzeba użyć końcowy, który Jon już wspomniano:
Jeśli utworzysz anonimową klasę wewnętrzną w swojej metodzie i użyjesz zmiennej lokalnej (takiej jak parametr metody) w tej klasie, kompilator zmusi cię, aby parametr był ostateczny:
Tutaj
from
to
parametry i muszą być ostateczne, aby można je było wykorzystać w anonimowej klasie.Powodem tego wymogu jest to, że zmienne lokalne znajdują się na stosie, dlatego istnieją tylko podczas wykonywania metody. Jednak anonimowa instancja klasy jest zwracana z metody, więc może żyć znacznie dłużej. Nie można zachować stosu, ponieważ jest on potrzebny do kolejnych wywołań metod.
Zatem Java zamiast tego umieszcza kopie tych zmiennych lokalnych jako zmienne instancji ukrytych w anonimowej klasie (możesz je zobaczyć, jeśli zbadasz kod bajtowy). Ale gdyby nie były ostateczne, można by oczekiwać, że klasa anonimowa i metoda widząca zmiany wprowadzą drugą zmienną do zmiennej. Aby zachować złudzenie, że jest tylko jedna zmienna zamiast dwóch kopii, musi być ostateczna.
źródło
final
parametrem funkcji a niefunkcjonalnym parametrem funkcji w oczach HotSpot?Cały czas używam ostatecznego parametru.
Czy to tyle dodaje? Nie całkiem.
Czy mógłbym to wyłączyć? Nie.
Powód: Znalazłem 3 błędy, w których ludzie napisali niechlujny kod i nie ustawili zmiennej członka w akcesoriach. Wszystkie błędy okazały się trudne do znalezienia.
Chciałbym, aby stało się domyślnym w przyszłej wersji Javy. Przekazywanie według wartości / referencji potyka się o wiele młodszych programistów.
Jeszcze jedno ... moje metody mają zwykle małą liczbę parametrów, więc dodatkowy tekst w deklaracji metody nie stanowi problemu.
źródło
Zastosowanie parametru final w metodzie metody nie ma nic wspólnego z tym, co dzieje się z argumentem po stronie wywołującej. Ma to jedynie oznaczać, że nie zmienia się w tej metodzie. Kiedy próbuję przyjąć bardziej funkcjonalny styl programowania, dostrzegam w tym wartość.
źródło
final
na parametry w interfejsach / deklaracjach metod abstrakcyjnych.Osobiście nie używam parametru końcowego dla parametrów metody, ponieważ powoduje to zbyt dużo bałaganu na listach parametrów. Wolę wymusić, aby parametry metody nie były zmieniane przez coś w rodzaju Checkstyle.
W przypadku zmiennych lokalnych używam finału, gdy tylko jest to możliwe, pozwalam nawet Eclipse robić to automatycznie w mojej konfiguracji dla osobistych projektów.
Z pewnością chciałbym czegoś mocniejszego, jak const C / C ++.
źródło
Ponieważ Java przekazuje kopie argumentów, uważam, że ich znaczenie
final
jest raczej ograniczone. Myślę, że nawyk pochodzi z ery C ++, w której można zabronić zmiany treści referencyjnych przez wykonanieconst char const *
. Uważam, że tego rodzaju rzeczy sprawiają, że uważasz, że deweloper jest z natury głupi jak cholera i musi być chroniony przed naprawdę każdą postacią, którą pisze. Z całą pokorą mogę powiedzieć, że piszę bardzo niewiele błędów, chociaż pomijamfinal
(chyba że nie chcę, aby ktoś zastąpił moje metody i klasy). Może jestem tylko oldschoolowym deweloperem.źródło
Nigdy nie używam finału na liście parametrów, po prostu dodaje bałaganu, jak powiedzieli poprzedni respondenci. Również w Eclipse możesz ustawić przypisanie parametrów w celu generowania błędu, więc użycie parametru final na liście parametrów wydaje mi się dość zbędne. Co ciekawe, gdy włączyłem ustawienie Eclipse dla przypisania parametrów generujących błąd, ten kod przechwycił ten kod (właśnie tak pamiętam przepływ, a nie rzeczywisty kod.): -
Grając adwokata diabła, co dokładnie jest złego w robieniu tego?
źródło
Krótka odpowiedź:
final
pomaga trochę, ale ... zamiast tego użyj defensywnego programowania po stronie klienta.Rzeczywiście problem
final
polega na tym, że wymusza tylko to, że odwołanie pozostaje niezmienione, co z radością pozwala mutować elementy obiektu, do którego istnieje odwołanie, bez wiedzy osoby wywołującej. Dlatego najlepszą praktyką w tym zakresie jest programowanie defensywne po stronie wywołującej, tworzenie głęboko niezmiennych instancji lub głębokich kopii obiektów, które mogą zostać zmasowane przez pozbawione skrupułów interfejsy API.źródło
Dodatkowym powodem dodania ostatecznego do deklaracji parametrów jest to, że pomaga zidentyfikować zmienne, których nazwy należy zmienić w ramach refaktoryzacji „Metoda ekstrakcji”. Przekonałem się, że dodanie wartości końcowej do każdego parametru przed rozpoczęciem refaktoryzacji dużej metody szybko mówi mi, czy są jakieś problemy, które muszę rozwiązać przed kontynuowaniem.
Na ogół jednak usuwam je jako zbędne na końcu refaktoryzacji.
źródło
Kontynuuj według posta Michela. Ja sam stworzyłem sobie inny przykład, aby to wyjaśnić. Mam nadzieję, że to może pomóc.
Z powyższego kodu w sposobie thisIsWhy () , faktycznie nie przypisać do [argumentu myObj obj] do realnego odniesienia w myparam. Zamiast tego używamy po prostu [argument MyObj obj] w metodzie wewnątrz MyParam.
Ale po zakończeniu metody thisIsWhy () , czy argument (obiekt) MyObj nadal istnieje?
Wydaje się, że tak powinno być, ponieważ możemy zobaczyć, że w dalszym ciągu wywołujemy metodę showObjName () i musi ona dotrzeć do obj . MyParam nadal używa / dociera do argumentu metody, nawet metoda już zwróciła!
To, jak Java naprawdę to osiąga, to generowanie kopii, jest także ukrytym odwołaniem do argumentu MyObj obj wewnątrz obiektu MyParam (ale nie jest to formalne pole w MyParam, więc nie możemy go zobaczyć)
Kiedy nazywamy „showObjName”, użyje tego odwołania, aby uzyskać odpowiednią wartość.
Ale jeśli nie umieścimy argumentu na końcu, co prowadzi do sytuacji, możemy ponownie przypisać nową pamięć (obiekt) do argumentu MyObj obj .
Technicznie w ogóle nie ma konfliktu! Jeśli możemy to zrobić, poniżej będzie sytuacja:
Bez konfliktu, ale „KONFUSUJĄCE !!” Ponieważ wszyscy używają tej samej „nazwy odwołania”, którą jest „obj” .
Aby tego uniknąć, ustaw go jako „końcowy”, aby programista nie wykonał kodu „podatnego na błędy”.
źródło