Kiedy należy użyć final dla parametrów metody i zmiennych lokalnych?

Znalazłem kilka referencji ( na przykład), które sugerują użycie final jak najwięcej i zastanawiam się, jak ważne jest to. Dzieje się tak głównie w kontekście parametrów metody i zmiennych lokalnych, a nie końcowych metod lub klas. Dla stałych ma to oczywisty sens.

Z jednej strony, kompilator może dokonać pewnych optymalizacji i to sprawia, że intencje programisty są jaśniejsze. Z drugiej strony dodaje zwięzłość, a optymalizacje mogą być trywialne.

Jest to coś, o czym powinienem pamiętać?

 151
Author: nawfal, 2008-09-30

16 answers

Obsess over:

  • pola końcowe-oznaczanie pól jako końcowych wymusza ich ustawienie do końca konstrukcji, co sprawia, że odniesienie do tego pola jest niezmienne. Pozwala to na bezpieczną publikację pól i pozwala uniknąć konieczności synchronizacji przy późniejszych odczytach. (Zauważ, że dla odniesienia do obiektu, tylko odniesienie do pola jest niezmienne - rzeczy, do których odnosi się odniesienie do obiektu, mogą nadal się zmieniać, co wpływa na niezmienność.)
  • końcowe pola statyczne-chociaż używam teraz enums dla wielu z przypadki, w których używałem statycznych pól końcowych.

Rozważ, ale używaj rozsądnie:

  • Final classes-Framework / API design jest jedynym przypadkiem, w którym to rozważam.
  • metody końcowe-zasadniczo takie same jak Klasy końcowe. Jeśli używasz szablonowych wzorców metod, takich jak crazy i oznaczanie rzeczy ostatecznych, prawdopodobnie polegasz zbytnio na dziedziczeniu, a nie wystarczająco na delegowaniu.

Ignoruj chyba, że czujesz anal:

  • parametry metody i lokalne zmienne-rzadko to robię głównie dlatego, że jestem leniwy i uważam, że to zaśmieca kod. W pełni przyznam, że oznaczanie parametrów i zmiennych lokalnych, których nie zamierzam modyfikować, jest "prawowite". Chciałbym, żeby to było domyślne. Ale tak nie jest i Kod jest trudniejszy do zrozumienia przy finałach. Jeśli jestem w cudzym kodzie, nie zamierzam go wyciągać, ale jeśli piszę nowy kod, nie umieszczę go. Jednym z wyjątków jest przypadek, w którym musisz zaznaczyć coś ostatecznego, aby móc uzyskaj dostęp z anonimowej klasy wewnętrznej.
 154
Author: Alex Miller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 19:14:50
Czy to jest coś, o czym powinienem pamiętać?

Nie, jeśli używasz Eclipse, ponieważ możesz skonfigurować akcję Save, aby automatycznie dodać te końcowe modyfikatory dla Ciebie. Wtedy otrzymujesz korzyści przy mniejszym wysiłku.

 43
Author: Peter Hilton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 18:31:04

Korzyści związane z czasem rozwoju "final" są co najmniej tak samo znaczące jak korzyści związane z czasem eksploatacji. Mówi przyszłym redaktorom kodu coś o twoich zamiarach.

Oznaczenie klasy "final" oznacza, że podczas projektowania lub implementacji klasy nie podjąłeś wysiłku, aby z gracją obsłużyć rozszerzenie. Jeśli czytelnicy mogą wprowadzić zmiany w klasie i chcą usunąć" ostateczny " modyfikator, mogą to zrobić na własne ryzyko. To do nich należy upewnienie się, że Klasa poradzi sobie dobrze przedłużony.

Oznaczenie zmiennej "final" (i przypisanie jej w konstruktorze) jest przydatne przy wstrzykiwaniu zależności. Wskazuje na" kolaboracyjny " charakter zmiennej.

Oznaczanie metody "final" jest przydatne w klasach abstrakcyjnych. Wyraźnie określa, gdzie znajdują się punkty rozszerzenia.

 12
Author: Eric R. Rath,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 19:06:11

Znalazłem oznaczanie parametrów metody i miejsc jako final jest przydatne jako pomoc refaktoryzacyjna, gdy dana metoda jest niezrozumiałym bałaganem na kilka stron. Posypać final obficie, zobaczyć, jakie błędy "nie można przypisać do zmiennej końcowej" wyrzuca kompilator (lub Twoje IDE), i po prostu może odkryć, dlaczego zmienna o nazwie "data" kończy się null, mimo że kilka (nieaktualnych) komentarzy przysięgają, że nie może się zdarzyć.

Następnie możesz naprawić niektóre błędy, zastępując ponownie używane zmienne z nowymi zmiennymi zadeklarowanymi bliżej punktu użycia. Potem okazuje się, że można owinąć całe części metody w szelki scoping, i nagle jesteś o jedną klawiaturę IDE od "Extract Method" i twój potwór po prostu stał się bardziej zrozumiały.

Jeśli twoja metoda jest a nie już nie do utrzymania wrakiem, myślę, że może być wartość w uczynieniu rzeczy ostatecznymi, aby zniechęcić ludzi do przekształcania go w wspomniany wrak; ale jeśli jest to krótka metoda (Patrz: nie do utrzymania), ryzykujesz dodaję dużo werbalności. W szczególności, podpisy funkcji Java są wystarczająco trudne, aby zmieścić się w 80 znaków, jak to jest bez dodawania sześciu więcej na argument!

 7
Author: Sam Stokes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-01 01:41:39

Cóż, to wszystko zależy od Twojego stylu... jeśli chcesz zobaczyć finał, gdy nie będziesz modyfikować zmiennej, użyj go. Jeśli nie lubisz tego oglądać... więc zostaw to.

Osobiście lubię jak najmniej zwięzłości, więc staram się unikać używania dodatkowych słów kluczowych, które nie są naprawdę konieczne.

Preferuję jednak języki dynamiczne, więc pewnie nie dziwi mnie, że lubię unikać słowności.

Więc powiedziałbym, że po prostu wybierz kierunek, w którym się skłaniasz i po prostu idź z nim (niezależnie od przypadku, staraj się być konsekwentny).


Na marginesie, pracowałem nad projektami, które używają i nie używają takiego wzorca, i nie widziałem różnicy w ilości błędów lub błędów... Nie sądzę, że jest to wzorzec, który znacznie poprawi liczbę błędów lub cokolwiek, ale znowu jest to styl, i jeśli lubisz wyrażać intencję, że nie będziesz go modyfikować, to śmiało go używaj.

 6
Author: Mike Stone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 18:31:56

Używam final cały czas, aby Java była bardziej oparta na wyrażeniach. Zobacz warunki Javy (if,else,switch) nie są oparte na wyrażeniach, których zawsze nienawidziłem, zwłaszcza jeśli używałeś do programowania funkcyjnego(np. ML, Scala lub Lisp).

Dlatego powinieneś starać się zawsze (IMHO) używać ostatecznych zmiennych podczas używania warunków.

Dam ci przykład:

    final String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            break;
        case JOB_POSTING_IMPORT:
            name = "Blah";
            break;
        default:
            throw new IllegalStateException();
    }

Teraz, jeśli dodasz kolejną case instrukcję i nie ustawisz name kompilator się nie powiedzie. Kompilator również zawiedzie, jeśli zrobisz nie łamać w każdym przypadku (że ustawiłeś zmienną). Pozwala to na upodobnienie Javy do wyrażeń let Lispa i sprawia, że kod nie jest masowo wcięty (ze względu na leksykalne zmienne zakresowe).

I jak zauważył @Recurse (ale widocznie -1 mnie) możesz zrobić poprzedzający z out making String name final aby uzyskać błąd kompilatora (którego nigdy nie powiedziałem, że nie możesz), ale możesz łatwo sprawić, że błąd kompilatora zniknie ustawienie nazwy po instrukcji switch, która wyrzuca semantyka wyrażenia lub, co gorsza, zapominanie o break, którego nie można spowodować błędu (pomimo tego, co mówi @Recurse) bez użycia final:

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            //break; whoops forgot break.. 
            //this will cause a compile error for final ;P @Recurse
        case JOB_POSTING_IMPORT:
            name = "Blah";
            break;
    }
    // code, code, code
    // Below is not possible with final
    name = "Whoops bug";

Ze względu na nazwę ustawienia błędu (poza zapomnieniem o break który również inny błąd) mogę teraz przypadkowo zrobić to:

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            break;
        //should have handled all the cases for pluginType
    }
    // code, code, code
    // Below is not possible with final
    name = "Whoops bug";

Ostateczna zmienna wymusza pojedynczą ocenę, jaka nazwa powinna być. Podobnie jak funkcja, która ma zwracaną wartość, musi zawsze zwracać wartość (ignorując wyjątki), blok przełączników nazw będzie miał aby rozwiązać nazwę i tym samym powiązać z tym blokiem przełącznika, co ułatwia refaktoryzację fragmentów kodu(np. metoda Eclipe refactor: extract).

Powyżej w OCaml:

type plugin = CandidateExport | JobPostingImport

let p = CandidateExport

let name = match p with
    | CandidateExport -> "Candidate Stuff"
    | JobPostingImport -> "Blah" ;;

match ... with ... oblicza się jak wyrażenie funkcji ie. Zwróć uwagę, jak wygląda nasza instrukcja switch.

Oto przykład w Scheme (rakieta lub kurczak):

(define name 
    (match b
      ['CandidateExport "Candidate Stuff"]
      ['JobPostingImport "Blah"]))
 6
Author: Adam Gent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-12 04:27:18

Jeśli piszesz aplikację, że ktoś będzie musiał odczytać kod po, powiedzmy, 1 roku, to tak, użyj final na zmiennej, która nie powinna być modyfikowana przez cały czas. Robiąc to, Twój kod będzie bardziej "samodokumentujący się", a także zmniejszysz szansę innym programistom na zrobienie głupich rzeczy, takich jak używanie stałej lokalnej jako lokalnej zmiennej tymczasowej.

Jeśli piszesz jakiś kod, to nie trudź się identyfikacją wszystkich stałych i uczynieniem ich ostatecznymi.

 4
Author: Alvin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 19:17:52

Jest to przydatne w parametrach, aby uniknąć przypadkowej zmiany wartości parametru i wprowadzić subtelny błąd. Używam, aby zignorować tę rekomendację, ale po spędzeniu około 4 godzin. w okropnej metodzie (z setkami linii kodu i wieloma forami, zagnieżdżonymi ifs i wszelkiego rodzaju złymi praktykami) polecam ci to zrobić.

 public int processSomethingCritical( final int x, final int y ){
 // hundreds of lines here 
     // for loop here...
         int x2 = 0;
        x++; // bug aarrgg...
 // hundreds of lines there
 // if( x == 0 ) { ...

 }
Oczywiście w idealnym świecie to by się nie stało, ale.. cóż.. czasami trzeba wspierać inne kod. :(
 4
Author: OscarRyz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-01 01:55:52

Użyję final jak najwięcej. Zrobi to flaga, jeśli nieumyślnie zmienisz pole. Ustawiam również parametry metody na final. W ten sposób złapałem kilka błędów z kodu, który przejąłem, gdy próbują "ustawić" parametr zapominając Java przechodzi przez wartość.

 3
Author: Javamann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 19:22:02

Nie jest jasne, czy jest to oczywiste, ale ostateczne określenie parametru metody wpływa tylko na ciało metody. To nie przekazać żadnych interesujących informacji na temat intencji metody do wywołującego. Przekazywany obiekt nadal może być zmutowany wewnątrz metody( finały nie są constami), a zakres zmiennej znajduje się wewnątrz metody.

Aby odpowiedzieć na Twoje dokładne pytanie, nie zawracałbym sobie głowy tworzeniem instancji lub zmiennej lokalnej (w tym parametry metody), chyba że Kod tego wymagał (np. zmienna jest odwołana z klasy wewnętrznej), lub w celu wyjaśnienia jakiejś naprawdę skomplikowanej logiki.

Na przykład zmienne, uczyniłbym je ostatecznymi, jeśli są logicznie stałymi.

 2
Author: ykaganovich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-01 01:09:09

Istnieje wiele zastosowań dla zmiennej final. Oto tylko kilka

Stałe Końcowe

 public static class CircleToolsBetter {
     public final static double PI = 3.141;
        public double getCircleArea(final double radius) {
          return (Math.pow(radius, 2) * PI);
        }
    }

To może być używane do innych części kodu, lub dostępne dla innych klas, w ten sposób, jeśli kiedykolwiek zmienisz wartość, nie będziesz musiał zmieniać ich jeden po drugim.

Zmienne Końcowe

public static String someMethod(final String environmentKey) {
    final String key = "env." + environmentKey;
    System.out.println("Key is: " + key);
    return (System.getProperty(key));

  }

}

W tej klasie budujesz zmienną końcową o zasięgu, która dodaje prefiks do parametru environmentKey. W tym przypadku finał zmienna jest ostateczna tylko w zakresie wykonania, który jest inny przy każdym wykonaniu metody. Za każdym razem, gdy metoda jest wprowadzana, finał jest rekonstruowany. Gdy tylko zostanie skonstruowana, nie może być zmieniana podczas wykonywania metody. Pozwala to na ustalenie zmiennej w metodzie na czas jej trwania. patrz poniżej:

public class FinalVariables {


  public final static void main(final String[] args) {
    System.out.println("Note how the key variable is changed.");
    someMethod("JAVA_HOME");
    someMethod("ANT_HOME");
  }
}

Stałe Końcowe

public double equation2Better(final double inputValue) {
    final double K = 1.414;
    final double X = 45.0;

double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M);
double powInputValue = 0;         
if (result > 360) {
  powInputValue = X * Math.sin(result); 
} else {
  inputValue = K * Math.sin(result);   // <= Compiler error   
}

Są one szczególnie przydatne, gdy masz naprawdę długie linie kodów i będzie Wygeneruj błąd kompilatora, aby nie uruchomić się do logic/business error, gdy ktoś przypadkowo zmieni zmienne, które nie powinny być zmieniane.

Zbiory Końcowe

Inny przypadek kiedy mówimy o kolekcjach, musisz ustawić je jako niemodyfikowalne.

 public final static Set VALID_COLORS; 
    static {
      Set temp = new HashSet( );
      temp.add(Color.red);
      temp.add(Color.orange);
      temp.add(Color.yellow);
      temp.add(Color.green);
      temp.add(Color.blue);
      temp.add(Color.decode("#4B0082")); // indigo
      temp.add(Color.decode("#8A2BE2")); // violet
      VALID_COLORS = Collections.unmodifiableSet(temp);
    }

W przeciwnym razie, jeśli nie ustawisz go jako niezmodyfikowany:

Set colors = Rainbow.VALID_COLORS;
colors.add(Color.black); // <= logic error but allowed by compiler

Klasy końcowe i metody końcowe nie mogą być rozszerzane ani zastępowane odpowiednio.

EDIT:ABY ROZWIĄZAĆ KOŃCOWY PROBLEM KLASY DOTYCZĄCY ENKAPSULACJI:

Są dwa sposoby na zakończenie zajęć. Pierwszym jest użycie słowa kluczowego final w deklaracji klasy:

public final class SomeClass {
  //  . . . Class contents
}

Drugim sposobem na zakończenie klasy jest zadeklarowanie wszystkich jej konstruktorów jako prywatnych:

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

Oznaczanie go jako końcowy oszczędza ci kłopotu, jeśli dowiesz się, że jest to ostateczny, aby zademonstrować spojrzenie na tę klasę testową. na początku wygląda publicznie spójrz.

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

Niestety, ponieważ jedynym konstruktorem klasy jest prywatny, nie jest możliwe rozszerzenie tej klasy. W przypadku klasy testowej nie ma powodu, aby klasa była ostateczna. Klasa testowa jest dobrym przykładem tego, jak Ukryte klasy końcowe mogą powodować problemy.

Więc powinieneś oznaczyć ją jako ostateczną, gdy w domyśle uczynisz klasę ostateczną, czyniąc jej konstruktor prywatnym.

 2
Author: mel3kings,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-21 06:40:29

Trochę kompromisu, jak wspomniałeś, ale wolę wyraźne użycie czegoś niż dorozumiane użycie. Pomoże to usunąć pewne niejasności dla przyszłych opiekunów kodu - nawet jeśli jesteś tylko Ty.

 1
Author: Sean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-30 18:38:05

Jeśli masz wewnętrzne (anonimowe) klasy, A Metoda musi uzyskać dostęp do zmiennej zawierającej metodę, musisz mieć tę zmienną jako ostateczną.

Poza tym, to co powiedziałeś jest słuszne.

 1
Author: anjanb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-10 18:45:42

Użyj słowa kluczowego final dla zmiennej, jeśli tworzysz tę zmienną jako immutable

Deklarując zmienną jako ostateczną, pomaga programistom wykluczyć ewentualne problemy modyfikacji zmiennych w środowisku wielowątkowym.

W wersji java 8 mamy jeszcze jedną koncepcję o nazwie "effectively final variable". zmienna niekończąca może być zmienną końcową.

zmienne lokalne odwołujące się do wyrażenia lambda muszą być ostateczne lub skuteczne finał

Zmienna jest rozważana skuteczny finał jeżeli nie jest modyfikowany po inicjalizacji w bloku lokalnym. Oznacza to, że możesz teraz używać zmiennej lokalnej bez końcowego słowa kluczowego wewnątrz anonimowej klasy lub wyrażenia lambda, pod warunkiem, że będą one ostatecznie ostateczne.

do Javy 7 nie można używać niekończącej się zmiennej lokalnej wewnątrz anonimowej klasy, ale od Javy 8 Można

Zobacz też artykuł

 0
Author: Ravindra babu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-28 13:33:25

Bardzo prosta odpowiedź mamy 3 przypadki z Final ze zmiennymi, Final z metodami & & Final z klasami..

1.Finał ze zmienną: u nie można przypisać tej zmiennej więcej niż jeden raz..

2.Koniec z metodami: u nie można nadpisać tej metody..

3.Finał z klasami: u can ' t extend any final class

 0
Author: Mohamed Ayed,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-19 03:15:28

Po pierwsze, końcowe słowo kluczowe jest używane do stworzenia stałej zmiennej. Stała oznacza, że się nie zmienia. Na przykład:

final int CM_PER_INCH = 2.54;

Zadeklarowałbyś ostateczną zmienną, ponieważ centymetr na cal się nie zmienia.

Jeśli spróbujesz nadpisać wartość końcową, zmienna jest tym, co została zadeklarowana jako pierwsza. Na przykład:

final String helloworld = "Hello World";
helloworld = "A String"; //helloworld still equals "Hello World"

Występuje błąd kompilacji, który jest czymś w rodzaju:

local variable is accessed from inner class, must be declared final

Jeśli twoja zmienna nie może być zadeklarowana jako ostateczna lub jeśli nie chcesz jej zadeklarować ostatnia próba:

final String[] helloworld = new String[1];
helloworld[0] = "Hello World!";
System.out.println(helloworld[0]);
helloworld[0] = "A String";
System.out.println(helloworld[0]);

To wydrukuje:

Hello World!
A String
 -1
Author: Jared Burrows,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-25 16:05:10