Dlaczego powinienem używać słowa kluczowego „final” w parametrze metody w Javie?

381

Nie rozumiem, gdzie finalsł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;
}
Basil Bourque
źródło
101
Jeden szybki punkt na temat terminologii - Java w ogóle nie ma referencji. Ma wartość referencyjną według wartości, która nie jest tym samym. Przy semantyce true pass by reference wyniki twojego kodu byłyby inne.
Jon Skeet
6
Jaka jest różnica między przekazywaniem przez odniesienie a przekazywaniem przez wartość?
NobleUplift
Łatwiej jest opisać tę różnicę w kontekście C (przynajmniej dla mnie). Jeśli przekażę wskaźnik do metody takiej jak: <code> int foo (int bar) </code>, wówczas wskaźnik ten jest przekazywany przez wartość. Oznacza to, że jest kopiowany, więc jeśli zrobię coś wewnątrz tej metody, na przykład <code> free (bar); bar = malloc (...); </code> to właśnie zrobiłem naprawdę złą rzecz. Bezpłatne połączenie faktycznie uwolni wycelowaną pamięć (więc dowolny wskaźnik, który przekazałem, teraz zwisa). Jednak <code> int foo (int & bar) </bar> oznacza, że ​​kod jest prawidłowy, a wartość przekazywanego wskaźnika zostanie zmieniona.
jerslan
1
Pierwszy powinien być int foo(int* bar)i ostatni int foo(int* &bar). Drugi przekazuje wskaźnik przez referencję, pierwszy przekazuje referencję według wartości.
jerslan
2
@Martin, moim zdaniem, to dobre pytanie ; zobacz tytuł pytania i treść postu jako wyjaśnienie, dlaczego pytanie jest zadawane. Może nie rozumiem tutaj reguł, ale właśnie tego chciałem podczas wyszukiwania „zastosowań ostatecznych parametrów w metodach” .
Victor Zamanian

Odpowiedzi:

239

Zatrzymaj zmianę przypisania zmiennej

Chociaż te odpowiedzi są interesujące intelektualnie, nie przeczytałem krótkiej prostej odpowiedzi:

Użyj słowa kluczowego final, jeśli chcesz, aby kompilator nie mógł zostać ponownie przypisany do innego obiektu.

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.

// Example use of this method: 
//   this.doSomething( "tiger" );
void doSomething( String arg ) {
  String x = arg;   // Both variables now point to the same String object.
  x = "elephant";   // This variable now points to a different String object.
  arg = "giraffe";  // Ditto. Now neither variable points to the original passed String.
}

Oznacz zmienną lokalną jako końcową . Powoduje to błąd kompilatora.

void doSomething( String arg ) {
  final String x = arg;  // Mark variable as 'final'.
  x = "elephant";  // Compiler error: The final local variable x cannot be assigned. 
  arg = "giraffe";  
}

Zamiast tego oznaczmy zmienną parametru jako końcową . To również powoduje błąd kompilatora.

void doSomething( final String arg ) {  // Mark argument as 'final'.
  String x = arg;   
  x = "elephant"; 
  arg = "giraffe";  // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}

Morał historii:

Jeśli chcesz mieć pewność, że zmienna zawsze wskazuje ten sam obiekt, zaznacz zmienną jako końcową .

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 finaldo wszystkich argumentów?

Czy powinniśmy dodawać finaldo każdego deklarowanego parametru metody?

  • Teoretycznie tak.
  • W praktyce nie.
    ➥ Dodawaj finaltylko 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ć finaldo 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ę finaltylko 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

public class MyClass {
    private int x;
    //getters and setters
}

void doSomething( final MyClass arg ) {  // Mark argument as 'final'.

   arg =  new MyClass();  // Compiler error: The passed argument variable arg  cannot be re-assigned to another object.

   arg.setX(20); // allowed
  // We can re-assign properties of argument which is marked as final
 }
Basil Bourque
źródło
23
„Jako dobrą praktykę programowania (w dowolnym języku), nigdy nie powinieneś ponownie przypisywać zmiennej parametru / argumentu [..]” Przykro mi, ale naprawdę muszę cię wezwać w tej sprawie. Ponowne przypisywanie argumentów jest standardową praktyką w językach takich jak Javascript, gdzie liczba przekazywanych argumentów (lub nawet jeśli są przekazywane) nie jest podyktowana podpisem metody. Na przykład podany podpis: „function say (msg)” ludzie upewnią się, że przypisany jest argument „msg”, podobnie jak: „msg = msg || 'Hello World!';”. Najlepsi programiści Javascript na świecie łamią waszą dobrą praktykę. Po prostu przeczytaj źródło jQuery.
Stijn de Witt
37
@StijndeWitt Twój przykład pokazuje sam problem ponownego przypisania zmiennej argumentu. Tracisz informacje, a nic nie zyskujesz w zamian: (a) Straciłeś pierwotną przekazaną wartość, (b) Straciłeś zamiar metody wywoływania (Czy osoba dzwoniąca przeszła „Hello World!”, Czy też domyślnie). Zarówno a & b są przydatne do testowania, długiego kodu i kiedy wartość jest dalej zmieniana później. Podtrzymuję moje oświadczenie: arg vars nigdy nie powinien być ponownie przydzielany. Kod powinien być: message = ( msg || 'Hello World"' ). Po prostu nie ma powodu, aby nie używać osobnego var. Jedyny koszt to kilka bajtów pamięci.
Basil Bourque,
9
@Basil: Liczy się więcej kodu (w bajtach) i JavaScript. Ciężko. Podobnie jak w przypadku wielu rzeczy, opiera się na opiniach. Całkowicie można całkowicie zignorować tę praktykę programowania i nadal pisać doskonały kod. Praktyka programowania jednej osoby nie sprawia, że ​​jest to praktyka każdego. Trzymaj się tego, co chcesz, i tak postanowię napisać inaczej. Czy to czyni mnie złym programistą, czy mój kod jest zły?
Stijn de Witt
14
Korzystanie message = ( msg || 'Hello World"' )ryzykuje mnie później przypadkowo msg. 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ą) .
Beni Cherniavsky-Paskin
6
@ BeniCherniavsky-Paskin ryzyko, które opisujesz, wynika wyłącznie z podobieństwa między messagei msg. Ale jeśli nazwałby to czymś takim processedMsglub 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. ;)
alfasin
230

Czasami miło jest wyrazić (dla czytelności), że zmienna się nie zmienia. Oto prosty przykład, w którym używanie finalmoże zaoszczędzić kilka potencjalnych problemów:

public void setTest(String test) {
    test = test;
}

Jeśli zapomnisz słowo kluczowe „to” na seter, zmienna, którą chcesz ustawić, nie zostanie ustawiona. Jeśli jednak użyjesz finalsłowa kluczowego w parametrze, błąd zostanie przechwycony podczas kompilacji.

Jerry Sha
źródło
65
btw zobaczysz ostrzeżenie „Przypisanie do testu zmiennej i tak nie działa”
AvrDragon
12
@AvrDragon Ale możemy również zignorować ostrzeżenie. Dlatego zawsze lepiej jest mieć coś, co powstrzyma nas przed pójściem dalej, na przykład błąd kompilacji, który otrzymamy, używając końcowego słowa kluczowego.
Sumit Desai
10
@AvrDragon To zależy od środowiska programistycznego. W każdym razie nie powinieneś polegać na IDE, aby złapać takie rzeczy, chyba że chcesz rozwinąć złe nawyki.
b1nary.atr0phy
25
@ b1naryatr0phy w rzeczywistości jest to ostrzeżenie kompilatora, a nie tylko wskazówka IDE
AvrDragon
8
@SumitDesai „Ale możemy też zignorować ostrzeżenie. Dlatego zawsze lepiej jest mieć coś, co powstrzyma nas przed pójściem dalej, na przykład błąd kompilacji, który otrzymamy, używając końcowego słowa kluczowego.” Rozumiem twój punkt widzenia, ale jest to bardzo mocne stwierdzenie, które moim zdaniem wielu programistów Java nie zgodzi się z tym. Ostrzeżenia kompilatora istnieją z jakiegoś powodu, a kompetentny programista nie powinien potrzebować błędu, aby „zmusić” ich do rozważenia jego konsekwencji.
Stuart Rossiter,
127

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 finalzmiennych 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 finalma 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?”

Jon Skeet
źródło
17
Ale parafrazując Krusty'ego swoim duńskim, co zrobili dla nas PÓŹNIEJ ? =)
James Schek
Yuval. Zabawne! Wydaje mi się, że pokój może się zdarzyć, nawet jeśli zostanie wymuszony ostrzem miecza!
gonzobrains
1
Pytanie wydaje się bardziej podobne do pytania „Czego Rzymianie nam nie zrobili?”, Ponieważ jest to raczej krytyka tego, czego nie robi ostatnie słowo kluczowe .
NobleUplift
„Czy sugerujesz, że istnieje znaczna liczba programistów, którzy sugerują, że finał ma większy efekt niż w rzeczywistości?” Dla mnie jest to główny problem: mocno podejrzewam, że znaczna część programistów korzystających z niego uważa, że ​​wymusza niezmienność przekazywanych elementów dzwoniącego, gdy tak nie jest. Oczywiście wówczas wciąga się w debatę na temat tego, czy standardy kodowania powinny „chronić przed” nieporozumieniami koncepcyjnymi (o których „kompetentny” deweloper powinien wiedzieć)), czy też nie (a następnie prowadzi to do opinii spoza zakresu SO pytanie typu)!
Stuart Rossiter,
1
@SarthakMittal: Wartość nie zostanie skopiowana, dopóki jej nie użyjesz, jeśli tak się zastanawiasz.
Jon Skeet
76

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:

public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
    return new Iterator<Integer>(){
        int index = from;
        public Integer next()
        {
            return index++;
        }
        public boolean hasNext()
        {
            return index <= to;
        }
        // remove method omitted
    };
}

Tutaj fromto 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.

Michael Borgwardt
źródło
1
Straciłeś mnie od „Ale jeśli nie są ostateczne ...” Czy możesz spróbować sformułować to inaczej, Może nie mam dość kawy.
hhafez
1
Masz lokalne zmienne z - pytanie brzmi, co się stanie, jeśli użyjesz instancji klasy anon wewnątrz metody i zmieni to wartość z - ludzie oczekiwaliby zmiany w metodzie, ponieważ widzą tylko jedną zmienną. Aby uniknąć tego zamieszania, musi być ono ostateczne.
Michael Borgwardt
Nie tworzy kopii, jest po prostu odniesieniem do dowolnego obiektu, do którego się odwołano.
vickirk
1
@vickirk: na pewno tworzy kopię referencji, w przypadku typów referencji.
Michael Borgwardt,
Btw, zakładając, że nie mamy anonimowych klas odwołujących się do tych zmiennych, czy wiesz, czy istnieje jakaś różnica między finalparametrem funkcji a niefunkcjonalnym parametrem funkcji w oczach HotSpot?
Pacerier
25

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.

Fortyrunner
źródło
4
Już miałem to zasugerować, aby końcowa wersja była domyślna w przyszłych wersjach i że musisz określić „zmienne” lub lepsze słowo kluczowe, które zostało wymyślone. Oto fajny artykuł na ten temat: lpar.ath0.com/2008/08/26/java-annoyance-final-parameters
Jeff Axelrod
Minęło dużo czasu, ale czy możesz podać przykład wykrytego błędu?
user949300
Zobacz najczęściej głosowanych na odpowiedź. To naprawdę dobry przykład, w którym zmienna składowa nie jest ustawiona, a zamiast tego parametr jest mutowany.
Fortyrunner
18

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ść.

Niemiecki
źródło
2
Dokładnie nie jest to część interfejsu funkcji, tylko implementacja. To mylące, że Java zezwala (ale dirsregards) final na parametry w interfejsach / deklaracjach metod abstrakcyjnych.
Beni Cherniavsky-Paskin
8

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 ++.

starblue
źródło
Nie jestem pewien, czy IDE i odniesienia do narzędzi mają zastosowanie do publikowania PO lub tematu. Mianowicie „końcowy” to sprawdzanie w czasie kompilacji, czy odwołanie nie zostało zmienione / napakowane. Ponadto, aby naprawdę egzekwować takie rzeczy, zobacz odpowiedź na temat braku ochrony podrzędnych członków końcowych odniesień. Na przykład podczas budowania interfejsu API użycie IDE lub narzędzia nie pomoże stronom zewnętrznym używającym / rozszerzającym taki kod.
Darrell Teague,
4

Ponieważ Java przekazuje kopie argumentów, uważam, że ich znaczenie finaljest raczej ograniczone. Myślę, że nawyk pochodzi z ery C ++, w której można zabronić zmiany treści referencyjnych przez wykonanie const 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ż pomijam final(chyba że nie chcę, aby ktoś zastąpił moje metody i klasy). Może jestem tylko oldschoolowym deweloperem.

Lawrence
źródło
1

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.): -

private String getString(String A, int i, String B, String C)
{
    if (i > 0)
        A += B;

    if (i > 100)
        A += C;

    return A;
}

Grając adwokata diabła, co dokładnie jest złego w robieniu tego?

Chris Milburn
źródło
Zachowaj ostrożność, aby odróżnić IDE od środowiska wykonawczego JVM. Cokolwiek robi IDE, nie ma znaczenia, gdy skompilowany kod bajtowy działa na serwerze, chyba że IDE dodał kod w celu ochrony przed wtargnięciem zmiennych składowych, takich jak błąd w kodzie, gdy zamierzano, aby zmienna nie była przypisywana, ale omyłkowo była - stąd cel ostatnie słowo kluczowe.
Darrell Teague,
1

Krótka odpowiedź: finalpomaga trochę, ale ... zamiast tego użyj defensywnego programowania po stronie klienta.

Rzeczywiście problem finalpolega 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.

Darrell Teague
źródło
2
„Problem z finałem polega na tym, że wymusza jedynie niezmienność odwołania” - nieprawda, sama Java temu zapobiega. Zmienna przekazana do metody nie może zostać zmieniona przez tę metodę.
Madbreaks
Przed opublikowaniem prosimy o sprawdzenie ... stackoverflow.com/questions/40480/
Darrell Teague,
Krótko mówiąc, gdyby to była prawda, że odniesienia do odniesienia nie może być zmieniony, nie byłoby omówienie obronnej-kopiowanie, niezmienności, nie ma potrzeby do ostatecznego słowa kluczowego, itp
Darrell Teague
Albo mnie źle rozumiesz, albo się mylisz. Jeśli przekażę odwołanie do obiektu do metody i metoda ta ponownie go przydzieli, oryginalne odwołanie pozostanie nienaruszone dla (mnie) obiektu wywołującego, gdy metoda zakończy wykonywanie. Java jest ściśle przekazywana przez wartość. I jesteś bezczelnie zarozumiały, twierdząc, że nie przeprowadziłem żadnych badań.
Madbreaks,
Oddanie głosu, ponieważ op zapytał, dlaczego warto użyć ostatecznego, a podałeś tylko jeden, nieprawidłowy powód.
Madbreaks,
0

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.

Michael Rutherfurd
źródło
-1

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.

public static void main(String[] args){
    MyParam myParam = thisIsWhy(new MyObj());
    myParam.setArgNewName();

    System.out.println(myParam.showObjName());
}

public static MyParam thisIsWhy(final MyObj obj){
    MyParam myParam = new MyParam() {
        @Override
        public void setArgNewName() {
            obj.name = "afterSet";
        }

        @Override
        public String showObjName(){
            return obj.name;
        }
    };

    return myParam;
}

public static class MyObj{
    String name = "beforeSet";
    public MyObj() {
    }
}

public abstract static class MyParam{
    public abstract void setArgNewName();
    public abstract String showObjName();
}

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:

  1. Mamy teraz ukryty punkt [MyObj obj], aby [Memory A in heap] teraz znajdował się w obiekcie MyParam.
  2. Mamy też inny [MyObj obj], który jest argumentem wskazującym, że [Memory B in heap] jest teraz dostępna w metodzie thisIsWhy.

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”.

KunYu Tsai
źródło