Ustawianie argumentów metody Java jako ostatecznych

92

Jaka to różnica finalmiędzy poniższym kodem. Czy jest jakaś korzyść z zadeklarowania argumentów jako final.

public String changeTimezone( Timestamp stamp, Timezone fTz, Timezone toTz){  
    return ....
}

public String changeTimezone(final Timestamp stamp, final Timezone fTz, 
        final Timezone toTz){
    return ....
}
Jan
źródło
4
Istnieją analizatory kodu, które ostrzegają, jeśli parametr zostanie ponownie użyty lub ponownie przypisany. (To samo dotyczy zmiennych lokalnych) IMHO, to lepszy sposób na wyłapanie takich parametrów, jeśli uznasz, że ich zmiana jest niepożądana.
Peter Lawrey,
Uważam, że Java powinna domyślnie ustawiać wszystkie argumenty metody wejściowej jako ostateczne. A jeśli chciałbym zmodyfikować odniesienie, musiałbym to zrobić ręcznie. W ten sposób współczynnik winy zapobiegłby wielu takim przypadkom.
Sid

Odpowiedzi:

131

Ponieważ formalny parametr metody jest zmienną lokalną, dostęp do nich można uzyskać z wewnętrznych klas anonimowych tylko wtedy, gdy są zadeklarowane jako końcowe.

Zapobiega to deklarowaniu kolejnej lokalnej zmiennej końcowej w treści metody:

 void m(final int param) {
        new Thread(new Runnable() {
            public void run() {
                System.err.println(param);
            }
        }).start();
    }
Peter Mmm
źródło
27
+1: To ważny przypadek użycia i jedyny raz, kiedy tego potrzebujesz . (Reszta czasu to tylko kwestia tego, co jest wygodne dla pomocy programistom.)
Donal Fellows,
3
Czy mogę zapytać powód tego?
KodeWarrior
21
Nie jest to już konieczne z Javą 8.
Amit Parashar
3
@simgineer przeczytaj tutaj: stackoverflow.com/questions/28408109/…
PeterMmm
3
@AmitParashar Prawda, ale Java 8 po prostu oszczędza Ci konieczności używania słowa kluczowego „final” za każdym razem, gdy musisz użyć zmiennej w klasie wewnętrznej ... W rzeczywistości kompilator po prostu utajnia ostateczność , nadal potrzebujesz zmiennej, aby była efektywnie ostateczna ... Więc nadal pojawia się błąd czasu kompilacji na wypadek, gdybyś próbował przypisać do niej później! Java 8 : SNEAK 100 :)
varun
38

Wyciąg z ostatniego słowa ostatniego słowa kluczowego

Parametry końcowe

Poniższy przykład deklaruje parametry końcowe:

public void doSomething(final int i, final int j)
{
  // cannot change the value of i or j here...
  // any change would be visible only inside the method...
}

final jest tutaj używany, aby zapewnić, że dwa indeksy i i j nie zostaną przypadkowo zresetowane metodą. To wygodny sposób na ochronę przed podstępnym błędem, który błędnie zmienia wartość Twoich parametrów. Ogólnie rzecz biorąc, krótkie metody są lepszym sposobem ochrony przed błędami tej klasy, ale parametry końcowe mogą być użytecznym dodatkiem do stylu kodowania.

Należy pamiętać, że parametry końcowe nie są uważane za część podpisu metody i są ignorowane przez kompilator podczas rozpoznawania wywołań metod. Parametry można zadeklarować jako końcowe (lub nie) bez wpływu na sposób nadpisywania metody.

pgras
źródło
18
W tym przykładzie lepiej byłoby użyć obiektów niż prymitywów, ponieważ zmiany pierwotne będą zawsze widoczne tylko wewnątrz metody. A w przypadku obiektów nadal możesz je zmienić. Po prostu nie możesz wskazać nowego obiektu. Właściwie teraz o tym myślę, final tak naprawdę niczego nie zmienia w porównaniu z pozostawieniem go poza tym, poza zapisaniem deklaracji zmiennej za pomocą AIC i nakłonieniem kompilatora do przypadkowych modyfikacji parametrów, których z jakiegoś powodu nie chcesz modyfikować .
Rob Grant
27

To ostatnie uniemożliwia przypisanie nowej wartości zmiennej, co może być pomocne w wyłapywaniu literówek. Stylistycznie możesz chcieć zachować otrzymane parametry bez zmian i przypisać je tylko do zmiennych lokalnych, więc final pomogłaby narzucić ten styl.

Muszę przyznać, że rzadko pamiętam o używaniu finału do parametrów, może powinienem.

public int example(final int basicRate){
    int discountRate;

    discountRate = basicRate - 10;
    // ... lots of code here 
    if ( isGoldCustomer ) {
        basicRate--;  // typo, we intended to say discountRate--, final catches this
    }
    // ... more code here

    return discountRate;
}
djna
źródło
1
Świetny przykład tego, jak przydatne może być uznanie argumentów za ostateczne. Jestem do tego stronniczy, ale są też pełne dla parametrów 3+.
JoseHdez_2,
14

Nie robi to dużej różnicy. To po prostu oznacza, że ​​nie możesz pisać:

stamp = null;
fTz = new ...;

ale nadal możesz napisać:

stamp.setXXX(...);
fTz.setXXX(...);

Jest to głównie wskazówka dla programisty konserwacyjnego, która podąża za Tobą, że nie zamierzasz przypisywać nowej wartości do parametru gdzieś w środku metody, gdzie nie jest to oczywiste i dlatego może powodować zamieszanie.

Adrian Pronk
źródło
3

Ostatnie słowo kluczowe użyte dla parametrów / zmiennych w Javie oznacza odwołanie jako ostateczne. W przypadku przekazania obiektu do innej metody system tworzy kopię zmiennej referencyjnej i przekazuje ją do metody. Oznaczając nowe referencje jako ostateczne, chronisz je przed ponownym przypisaniem. Czasami jest to uważane za dobrą praktykę kodowania.

Sid
źródło
1
Muszę coś dodać: jeśli parametry są prymitywne, to nie widzę żadnych różnic. Poza tym, jeśli parametrami są Kolekcje (lista obiektów ...), dodanie finału nie mogło zapobiec ich modyfikacji.
Sam003
1
Niezmienność jest zawsze pożądaną cechą. Java nie ma go po wyjęciu z pudełka. Doprowadzenie do ostateczności zmiennych zapewnia przynajmniej integralność odniesienia.
Sid
1
Zgadzam się. Ale jeśli naprawdę chcemy osiągnąć niezmienność obiektów, możemy spróbować stworzyć głęboki klon.
Sam003
2

W przypadku treści tej metody finalsłowo kluczowe zapobiegnie przypadkowemu przypisaniu odwołań do argumentów, powodując błąd kompilacji w tych przypadkach (większość IDE będzie narzekać od razu). Niektórzy mogą twierdzić, że finalogólne używanie, gdy tylko jest to możliwe, przyspieszy działanie, ale tak nie jest w przypadku najnowszych maszyn JVM.

dimitrisli
źródło
2

Dwie zalety, które widzę, są wymienione:

1 Oznaczanie argumentu metody jako ostatecznego zapobiega ponownemu przypisaniu argumentu wewnątrz metody

Z twojego przykładu

    public String changeTimezone(final Timestamp stamp, final Timezone fTz, 
            final Timezone toTz){
    
    // THIS WILL CAUSE COMPILATION ERROR as fTz is marked as final argument

      fTz = Calendar.getInstance().getTimeZone();     
      return ..
    
    }

W skomplikowanej metodzie oznaczanie argumentów jako ostatecznych pomoże w przypadkowej interpretacji tych argumentów jako metod zmiennych lokalnych, a ponowne przypisanie ich jako kompilatora będzie oznaczać te przypadki, jak pokazano w przykładzie.

2 Przekazanie argumentu do anonimowej klasy wewnętrznej

Ponieważ formalny parametr metody jest zmienną lokalną, dostęp do nich można uzyskać z wewnętrznych klas anonimowych tylko wtedy, gdy są zadeklarowane jako końcowe.

Santhosh Urumese
źródło
1

- W przeszłości (przed Java 8 :-))

Jawne użycie słowa kluczowego „final” wpłynęło na dostępność zmiennej metody dla wewnętrznych klas anonimowych.

- W nowoczesnym języku (Java 8+) nie ma takiej potrzeby:

Java wprowadziła zmienne „efektywnie końcowe”. Zakłada się, że zmienne lokalne i parametry metod są ostateczne, jeśli kod nie implikuje zmiany wartości zmiennej. Więc jeśli widzisz takie słowo kluczowe w Java8 +, możesz założyć, że jest zbędne. Wprowadzenie „efektywnego finału” powoduje, że przy stosowaniu lambd wpisujemy mniej kodu.

Witold Kaczurba
źródło
0

To tylko konstrukcja w Javie, która pomaga zdefiniować kontrakt i trzymać się go. Podobna dyskusja tutaj: http://c2.com/cgi/wiki?JavaFinalConsideredEvil

BTW - (jak mówi twiki), oznaczanie argumentów jako ostatecznych jest generalnie zbędne, jeśli postępujesz zgodnie z dobrymi zasadami programowania i wykonałeś ponowne przypisanie / przedefiniowanie przychodzącego odwołania do argumentu.

W najgorszym przypadku, jeśli ponownie zdefiniujesz odwołanie args, nie wpłynie to na rzeczywistą wartość przekazaną do funkcji - ponieważ przekazano tylko referencję.

madhurtanwani
źródło
0

Mówię ogólnie o oznaczaniu zmiennych i pól jako ostateczne - nie dotyczy to tylko argumentów metod. (Oznaczanie metod / klas końcowych to zupełnie inna sprawa).

To przysługa dla czytelników / przyszłych opiekunów Twojego kodu. Wraz z rozsądną nazwą zmiennej pomaga czytelnikowi twojego kodu zobaczyć / zrozumieć, co reprezentują dane zmienne - i zapewnia czytelnika, że ​​ilekroć zobaczysz zmienną w tym samym zakresie, znaczenie pozostaje. to samo, więc nie musi drapać się po głowie, aby zawsze dowiedzieć się, co oznacza zmienna w każdym kontekście. Widzieliśmy zbyt wiele nadużyć „ponownego wykorzystania” zmiennych, przez co nawet krótki fragment kodu jest trudny do zrozumienia.

PROMIEŃ
źródło
-3

Ostatnie słowo kluczowe zapobiega przypisaniu nowej wartości do parametru. Chciałbym to wyjaśnić na prostym przykładzie

Załóżmy, że mamy metodę

method1 () {

Date dateOfBirth = new Date („1/1/2009”);

method2 (dateOfBirth);

method3 (dateOfBirth); }

public mehod2 (DateOfBirth) {
....
....
....
}

public mehod2 (DateOfBirth) {
....
....
....
}

W powyższym przypadku, jeśli "dateOfBirth" zostanie przypisana nowa wartość w method2, to spowoduje to błędne wyjście z method3. Ponieważ wartość, która jest przekazywana do method3, nie jest tym, czym była przed przekazaniem do method2. Aby uniknąć tego ostatniego słowa kluczowego jest używane dla parametrów.

Jest to również jedna z najlepszych praktyk w zakresie kodowania w języku Java.

Kamal
źródło
5
To nie jest całkiem w porządku. Nawet jeśli argument dateOfBirth zostanie zmieniony na inną wartość w method2 (), nie będzie to miało wpływu na method2 (), ponieważ Java przekazuje wartość, a nie referencję.
Flo