Dlaczego słowo kluczowe „końcowe” jest tak rzadko używane w branży? [Zamknięte]

14

Ponieważ finalkilka lat temu odkryłem moc słowa kluczowego w Javie, pomogło mi to uczynić moje kody o wiele bardziej czytelnymi, ponieważ ludzie mogą łatwo zobaczyć, które zmienne są tylko do odczytu. Może to również nieco poprawić działanie JIT i chociaż jest to bardzo ograniczone, nie może zaszkodzić, szczególnie podczas atakowania na urządzenia wbudowane, takie jak platformy Android.

Ale przede wszystkim przyczynia się do uczynienia projektu bardziej odpornym na zmiany i kieruje innymi osobami odpowiedzialnymi za modyfikację bazy kodu.

Jednak podczas przeglądania kodu źródłowego JDK nigdy nie natknąłem się na to słowo kluczowe, ani w odniesieniu do klas, metod, ani zmiennych. To samo dotyczy różnych systemów, które musiałem sprawdzić.

Czy jest powód? Czy jest to powszechny paradygmat projektowania, aby wszystko można było modyfikować od wewnątrz?

Aurelien Ribon
źródło
w mikrooptymalizacjach ustawianie finalpól ma taką samą semantykę jak pisanie volatilepola, a następnie czytanie ich później musi mieć zmienną semantykę odczytu, to nie zawsze to, czego chcesz
maniak ratchet
5
@ratchet: Myślę, że chodzi tu bardziej o to, aby twoje klasy były niezmienne, tak jak w programowaniu funkcjonalnym.
Jonas
@Kwebble: Być może, co ważniejsze, poszczególne wystąpienia String są niezmienne.
MatrixFrog,

Odpowiedzi:

9

Podejrzewam, że powodem, dla którego go nie widzisz, jest to, że JDK został zaprojektowany do rozszerzenia (jest to podstawa, na której zbudowane są wszystkie twoje programy). Korzystanie z niego przez kod aplikacji nie byłoby niczym niezwykłym. Nie spodziewałbym się również, że zobaczę go dużo w kodzie biblioteki (ponieważ biblioteki są również często zaprojektowane do rozszerzenia).

Spójrz na kilka dobrych projektów open source i myślę, że znajdziesz więcej.

W przeciwieństwie do tego, co widzisz w Javie, w świecie .NET wielokrotnie słyszałem argument, że klauzule powinny być sealed(podobne do ostatecznego zastosowania do klasy) domyślnie i że programista powinien jawnie odblokować to.

Steven Evers
źródło
2
Miałem nawet na myśli wielu prywatnych lub chronionych członków niektórych klas JDK, które są oczywiście „odradzają się raz, nigdy nie zastępują” zmiennych obiektowych. Szczególnie w pakiecie huśtawkowym. Są to idealni kandydaci do „ostatecznego”, ponieważ zastąpienie wskaźnikiem zerowym prowadziłoby do błędów podczas korzystania ze składników.
Aurelien Ribon
1
@ Aurélien: Jeśli to sprawi, że poczujesz się lepiej, wiele klas w .NET Framework jest zamkniętych, ku konsternacji niektórych użytkowników, którzy chcieliby rozszerzyć klasy frameworków o własne funkcje. Ograniczenie to można jednak nieco złagodzić za pomocą metod rozszerzenia.
Robert Harvey
1
Myślę, że chodzi bardziej o niezmienność niż unikanie rozszerzenia. Innymi słowy użycie finalna polach, a nie na metodach. Niezmienne klasy mogą być również zaprojektowane do przedłużenia.
Jonas
15

Problem z używaniem final do przekazywania, że ​​coś jest tylko do odczytu, polega na tym, że tak naprawdę działa tylko dla prymitywnych typów, takich jak int, charitp. Do wszystkich obiektów w Javie faktycznie używa się wskaźnika (rodzaju). W rezultacie, gdy używasz finalsłowa kluczowego na obiekcie, mówisz tylko, że odwołanie jest tylko do odczytu, a sam obiekt jest nadal zmienny.

Mógłby zostać użyty częściej, gdyby faktycznie uczynił obiekt tylko do odczytu. W C ++ właśnie to constdziała, w wyniku czego jest to o wiele bardziej użyteczne i często używane słowo kluczowe.

Jednym miejscem, w którym często używam finalsłowa kluczowego, są parametry, aby uniknąć nieporozumień spowodowanych przez takie rzeczy:

public void someMethod(FancyObject myObject) {
    myObject = new FancyObject();
    myObject.setProperty(7);
}
...
public static void main(final String[] args) {
    ...
    FancyObject myObject = new FancyObject();
    someOtherObject.someMethod(myObject);
    myObject.getProperty(); // Not 7!
}

W tym przykładzie wydaje się oczywiste, dlaczego to nie działa, ale jeśli someMethod(FancyObject)jest duże i może dojść do skomplikowanego zamieszania. Dlaczego tego nie uniknąć?

Jest to również część standardów kodowania Sun (lub Oracle, jak sądzę teraz).

Gyan alias Gary Buyn
źródło
1
Jeśli finalklasy API Java były częściej używane , większość obiektów była niezmienna, jak w przypadku wielu bibliotek Scala. Następnie utworzenie pól obiektowych finalsprawia, że ​​klasa jest niezmienna w wielu przypadkach. Ten projekt jest już używany w BigDecimali String.
Jonas
@Jonas Przeczytałem pytanie, aby bardziej dotyczyło punktu 4 w odpowiedzi schmmda, ponieważ powiedział „skoro ludzie mogą łatwo zobaczyć, co to zmienne tylko do odczytu” i odpowiednio na nie odpowiedział. Może powinienem był uwzględnić to założenie w odpowiedzi.
Gyan alias Gary Buyn
Pamiętaj, że jest czas, kiedy chcesz ponownie przypisać parametr. Przykładem jest przycięcie parametru ciągu.
Steve Kuo,
@ Steve Zwykle tworzę nową zmienną do przypisania w tej sytuacji.
Gyan alias Gary Buyn
1
@Gary, przynajmniej dla mnie, bardzo rzadko zdarza się, że błędy / zamieszanie są spowodowane przez ponowne przypisanie parametru. Wolałbym mieć niepotrzebny kod i akceptować szansę na ponowne przypisanie parametru powodującego błąd. Zachowaj ostrożność podczas pisania / modyfikowania kodu.
Steve Kuo,
4

Zgadzam się, że final słowo kluczowe poprawia czytelność. Ułatwia to zrozumienie, kiedy obiekt jest niezmienny. Jednak w Javie jest bardzo gadatliwy, szczególnie (moim zdaniem), gdy jest używany w parametrach. Nie oznacza to, że ludzie nie powinni go używać, ponieważ jest pełny, ale raczej go nie używają, ponieważ jest pełny.

Niektóre inne języki, takie jak scala, znacznie ułatwiają składanie ostatecznych deklaracji ( val ). W tych językach końcowe deklaracje mogą być bardziej powszechne niż zmienne.

Pamiętaj, że istnieje wiele różnych zastosowań końcowego słowa kluczowego. Twój post obejmuje głównie pozycje 2 i 3.

  1. Klasy końcowe (JLS 8.1.1.2)
  2. końcowe pola (JLS 8.3.1.2)
  3. Metody końcowe (JLS 8.4.3.3)
  4. końcowe zmienne (JLS 4.12.4)
schmmd
źródło
2

Na początek oficjalne konwencje Java Code nie faworyzują ani nie zabraniają określonego użycia final. Oznacza to, że programiści JDK mogą dowolnie wybierać preferowany sposób.

Nie umiem czytać w ich myślach, ale dla mnie preferencja dotycząca tego, czy korzystać, finalczy nie, była kwestią skupioną. To, czy mam wystarczająco dużo czasu, aby całkowicie skoncentrować się na kodzie, czy nie .

  • Powiedzmy, że w jednym z moich projektów było nas stać na spędzanie średnio jednego dnia na około 100 liniach kodu. W tym projekcie miałem wyraźne postrzeganie finaljako śmieci, które po prostu zaciemniają rzeczy, które są już wystarczająco wyraźnie wyrażone w kodzie. Wygląda na to, że programiści JDK również należą do tej kategorii.
     
    Z drugiej strony było zupełnie odwrotnie w innym projekcie, w którym spędziliśmy godzinę na 100 liniach kodu. Tam zastałem strzelanie finaljak pistolet maszynowy we własnym i innym kodzie - po prostu dlatego, że był to najszybszy sposób na wykrycie zamiaru faceta, który napisał go przede mną, i, podobnie, najszybszy sposób na przekazanie moich własnych zamiarów facet, który później będzie pracował nad moim kodem.

Może również zapewnić trochę wzmocnienia JIT i chociaż jest to bardzo ograniczony, nie może zaszkodzić

Rozumowanie jak powyżej jest śliskie. Przedwczesna optymalizacja może zaszkodzić; Donald Knuth posuwa się nawet do nazwania go „źródłem wszelkiego zła” . Nie pozwól, żeby cię to uwięziło. Napisz głupi kod .

komar
źródło
2

Ostatnio odkryłem radość z funkcji „Zapisz działania” w środowisku Eclipse IDE. Mogę zmusić go do przeformatowania kodu, wstawienia brakujących @Overrideadnotacji i zrobienia kilku fajnych rzeczy, takich jak usuwanie niepotrzebnych nawiasów w wyrażeniach lub umieszczanie finalsłowa kluczowego wszędzie za każdym razem, gdy kliknę ctrl + S. Aktywowałem niektóre z tych wyzwalaczy i, chłopcze, to bardzo pomaga!

Okazało się, że wiele z tych wyzwalaczy działa jak szybkie sprawdzenie poprawności mojego kodu.

  • Zamierzałem zastąpić metodę, ale adnotacja nie pojawiła się po uderzeniu ctrl + s? - być może gdzieś schrzaniłem typy parametrów!
  • Niektóre nawiasy zostały usunięte z kodu podczas zapisywania? - może to wyrażenie logiczne jest zbyt trudne dla programisty, aby szybko się z nim obchodzić. W przeciwnym razie dlaczego miałbym dodawać te nawiasy w pierwszej kolejności?
  • Ten parametr lub zmienna lokalna nie jest final. Czy musi zmienić swoją wartość?

Okazało się, że im mniej zmiennych, tym mniej problemów mam w czasie debugowania. Ile razy śledziłeś wartość jakiejś zmiennej tylko po to, by stwierdzić, że zmienia się ona jakoś powiedzmy od 5 do 7? „Jak do cholery może być ?!” zadajesz sobie pytanie i spędzasz kolejne kilka godzin wchodząc i wychodząc z niezliczonych metod, aby dowiedzieć się, że popełniłeś błąd w swojej logice. Aby to naprawić, musisz dodać jeszcze jedną flagę, kilka warunków i ostrożnie zmienić niektóre wartości tu i tam.

Och, nienawidzę debugowania! Za każdym razem, gdy uruchamiam debuger, mam wrażenie, że mój czas się kończy i rozpaczliwie potrzebuję tego czasu, aby spełnić przynajmniej niektóre z moich marzeń z dzieciństwa! Do diabła z debugowaniem! finaloznacza koniec tajemniczych zmian wartości. Więcejfinal s => mniej wątłych części w moim kodzie => mniej błędów => więcej czasu na robienie dobrych rzeczy!

Jeśli chodzi o finalzajęcia i metody, nie obchodzi mnie to. Uwielbiam polimorfizm. Polimorfizm oznacza ponowne użycie oznacza mniej kodu oznacza mniej błędów. JVM i tak wykonuje całkiem dobrą robotę z dewitualizacją i wprowadzaniem metod, więc nie widzę wartości w zabijaniu możliwości ponownego użycia kodu dla nieoczekiwanych korzyści wydajnościowych.


Widzenie wszystkich tych finalznaków w kodzie na początku trochę rozprasza i wymaga czasu, aby się przyzwyczaić. Niektórzy z moich kolegów z zespołu wciąż są bardzo zaskoczeni, widząc tak wiele finalsłów kluczowych. Żałuję, że nie było w IDE ustawienia dla specjalnego kolorowania składni. Z przyjemnością przestawię go na odcień szarości (np. Adnotacje), aby nie rozpraszały się podczas czytania kodu. Eclipse ma obecnie osobny kolor dla returni wszystkich innych słów kluczowych, ale nie dla final.

Andrew Андрей Листочкин
źródło
1
Być może warto rozważyć użycie funkcjonalnych języków, takich jak OCaml lub F #. Te języki zwykle wymagają znacznie mniej debugowania, jednym z powodów jest to, że wszystko jest domyślnie tylko do odczytu. Mówiąc prosto, po przypisaniu zmienna w języku funkcjonalnym nie zmienia się.
Abel
1
Tak, wiem o tym i od czasu do czasu piszę trochę kodu ML - stąd czerpałem inspirację :) Nie chodzi o język, którego używasz, ale raczej o zasady i techniki, które stosujesz. Z Haskellem i donotacją można zrobić wszystko, co niezbędne : Jasne, Java nie jest najlepszym językiem do pisania w funkcjonalnym stylu, ale przynajmniej pozwala pisać solidny kod - i to jest to, na czym mi naprawdę zależy.
Andrew Андрей Листочкин