Metoda „final” w Javie: co obiecuje?

141

W klasie Java można zdefiniować metodę jako final, aby zaznaczyć, że ta metoda nie może zostać zastąpiona:

public class Thingy {
    public Thingy() { ... }
    public int operationA() {...}
    /** this method does @return That and is final. */
    public final int getThat() { ...}
}

To jasne i może być przydatne do ochrony przed przypadkowym zastąpieniem, a może występem - ale to nie jest moje pytanie.

Moje pytanie brzmi: Z punktu widzenia OOP zrozumiałem, że definiując metodę, finalprojektant klasy obiecuje, że ta metoda będzie zawsze działać zgodnie z opisem lub sugestią. Ale często może to być poza wpływem autora klasy, jeśli to, co robi metoda, jest bardziej skomplikowane niż po prostu dostarczenie właściwości .

Ograniczenie składniowe jest dla mnie jasne, ale jaka jest implikacja w sensie OOP? Czy finalw tym sensie jest używane poprawnie przez większość autorów klasowych?

Jaki rodzaj „umowy” finalobiecuje metoda?

towi
źródło

Odpowiedzi:

156

Jak wspomniano, finaljest używany z metodą Java, aby zaznaczyć, że metody nie można przesłonić (w przypadku zakresu obiektu) ani ukryć (w przypadku statycznego). Pozwala to pierwotnemu programiście tworzyć funkcje, których nie można zmienić za pomocą podklas i to jest cała gwarancja, jaką zapewnia.

Oznacza to, że jeśli metoda opiera się na innych konfigurowalnych składnikach, takich jak niepubliczne pola / metody, funkcjonalność ostatecznej metody może nadal być dostosowywana. Jest to jednak dobre, ponieważ (w przypadku polimorfizmu) umożliwia częściowe dostosowanie.

Istnieje wiele powodów, dla których nie można dostosowywać czegoś, w tym:

  • Wydajność - niektóre kompilatory mogą analizować i optymalizować działanie, zwłaszcza bez skutków ubocznych.

  • Uzyskaj hermetyzowane dane - spójrz na niezmienne obiekty, w których ich atrybuty są ustawione w czasie budowy i nigdy nie powinny być zmieniane. Lub obliczona wartość uzyskana z tych atrybutów. Dobrym przykładem jest Stringklasa Java .

  • Niezawodność zamówienia i - przedmioty składają się z pierwotnych ( int, char, double, etc.) i / lub inne obiekty. Nie wszystkie operacje mające zastosowanie do tych komponentów powinny być możliwe do zastosowania, a nawet logiczne, gdy są używane w większym obiekcie. finalAby to zapewnić, można użyć metod z modyfikatorem. Klasa Counter jest dobrym przykładem.


public class Counter {
    private int counter = 0;

    public final int count() {
        return counter++;
    }

    public final int reset() {
        return (counter = 0);
    }
}

Jeśli public final int count()metoda nie jest final, możemy zrobić coś takiego:

Counter c = new Counter() {   
    public int count() {
        super.count();   
        return super.count();   
    } 
}

c.count(); // now count 2

Lub coś w tym stylu:

Counter c = new Counter() {
    public int count() {
        int lastCount = 0;
        for (int i = super.count(); --i >= 0; ) {
            lastCount = super.count();
        }

        return lastCount;
    }
}

c.count(); // Now double count
NawaMan
źródło
27

Jaki rodzaj „umowy” obiecuje ostateczna metoda?

Spójrz na to z drugiej strony, każda metoda nieostateczna daje niejawną gwarancję, że możesz ją zastąpić własną implementacją, a klasa będzie nadal działać zgodnie z oczekiwaniami. Jeśli nie możesz zagwarantować, że Twoja klasa obsługuje nadpisywanie metody, powinieneś uczynić ją ostateczną.

josefx
źródło
Ale czy ten pogląd nie oznacza dla mojego pierwotnego pytania „ta ostateczna metoda zawsze będzie zachowywać się zgodnie z obietnicą”, że nie wolno mi nazywać żadnych nieostatecznych metod z wnętrza metody ostatecznej? Ponieważ, jeśli to zrobię, wywołana metoda mogła zostać nadpisana i dlatego nie mogę zagwarantować zachowania mojej ostatecznej metody?
towi
8

Przede wszystkim możesz oznaczyć nieabstrakcyjne klasy finaloraz pola i metody. W ten sposób cała klasa nie może być podklasą. Zatem zachowanie klasy zostanie naprawione.

Zgadzam się, że metody oznaczania finalnie gwarantują, że ich zachowanie będzie takie samo w podklasach, jeśli te metody wywołują metody nieostateczne. Jeśli zachowanie rzeczywiście wymaga naprawy, należy to osiągnąć poprzez konwencję i staranny projekt. I nie zapomnij o tym w javadoc! (Dokumentacja java)

Wreszcie, finalsłowo kluczowe odgrywa bardzo ważną rolę w Java Memory Model (JMM). JMM gwarantuje, że do uzyskania widoczności finalpól nie jest wymagana odpowiednia synchronizacja. Na przykład:

class A implements Runnable {
  final String caption = "Some caption";                           

  void run() {
    // no need to synchronize here to see proper value of final field..
    System.out.println(caption);
  }
}  
Victor Sorokin
źródło
tak wiem o końcowych zajęciach - łatwy przypadek. Dobra uwaga o końcowych polach z JMM, synchronizacja nie jest wymagana ... hmm: dotyczy to tylko "wskaźnika", prawda? Nadal mogłem zmodyfikować niezsynchronizowany obiekt, do którego się odnosi (ok, nie w String, ale w klasach zdefiniowanych przez użytkownika). Ale to, co powiedziałeś o „finale nie gwarantuje zachowania”, jest dokładnie moim celem. Zgadzam się, dokumentacja i projekt są ważne.
towi
@towi masz rację, finalco nie zapewni widoczności zmian wprowadzonych w obiektach złożonych, takich jak Map.
Victor Sorokin,
0

Nie jestem pewien, czy możesz poczynić jakiekolwiek zapewnienia na temat użycia terminu „wersja ostateczna” i jaki ma to wpływ na ogólną umowę projektową oprogramowania. Masz gwarancję, że żaden programista nie może zastąpić tej metody i unieważnić w ten sposób swojej umowy. Z drugiej strony, ostatnia metoda może polegać na zmiennych klasowych lub instancji, których wartości są ustawiane przez podklasy, i może wywoływać inne metody klas, które nadpisywane. Tak więc finał jest co najwyżej bardzo słabą gwarancją.

Jim Ferrans
źródło
1
Tak, o to mi chodziło. Dobrze. Podoba mi się termin „słaba gwarancja” :-) I (głównie) lubię C ++ const. Jak w char const * const = "Hello"lub char const * const addName(char const * const name) const...
towi
0

Nie, to nie jest poza wpływem autora zajęć. Nie możesz go przesłonić w swojej klasie pochodnej, dlatego zrobi to, co zamierzał autor klasy bazowej.

http://download.oracle.com/javase/tutorial/java/IandI/final.html

Warto zwrócić uwagę na część, w której sugeruje, że powinny być metody wywoływane z konstruktorów final.

Brian Roach
źródło
1
Cóż, technicznie rzecz biorąc, zrobi to, co napisał autor klasy bazowej .
Joey,