Java `final` Methode: Was verspricht es?

141

In einer Java-Klasse kann eine Methode definiert werden final, um zu kennzeichnen, dass diese Methode nicht überschrieben werden darf:

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

Das ist klar, und es kann nützlich sein, um sich vor versehentlichem Überschreiben oder vielleicht vor Leistung zu schützen - aber das ist nicht meine Frage.

Meine Frage lautet: Aus OOP-Sicht habe ich verstanden, dass finalder Klassenentwickler durch die Definition einer Methode verspricht, dass diese Methode immer wie beschrieben oder impliziert funktioniert. Aber oft kann dies außerhalb des Einflusses des Klassenautors liegen, wenn die Methode komplizierter ist, als nur eine Eigenschaft zu liefern .

Die syntaktische Einschränkung ist mir klar, aber was bedeutet das im OOP-Sinne? Wird finalin diesem Sinne von den meisten Klassenautoren richtig verwendet?

Welche Art von "Vertrag" finalverspricht eine Methode?

Towi
quelle

Antworten:

155

Wie bereits erwähnt, finalwird es mit einer Java-Methode verwendet, um zu markieren, dass die Methode nicht überschrieben (für den Objektbereich) oder ausgeblendet (für statisch) werden kann. Auf diese Weise kann der ursprüngliche Entwickler Funktionen erstellen, die von Unterklassen nicht geändert werden können. Dies ist die gesamte Garantie, die er bietet.

Dies bedeutet, dass die Funktionalität der endgültigen Methode möglicherweise noch anpassbar ist, wenn die Methode auf anderen anpassbaren Komponenten wie nicht öffentlichen Feldern / Methoden basiert. Dies ist jedoch gut, da es (mit Polymorphismus) eine teilweise Anpassung ermöglicht.

Es gibt eine Reihe von Gründen, die verhindern, dass etwas anpassbar ist, darunter:

  • Leistung - Einige Compiler können den Betrieb analysieren und optimieren, insbesondere der ohne Nebenwirkungen.

  • Erhalten Sie gekapselte Daten - sehen Sie sich unveränderliche Objekte an, deren Attribute zum Zeitpunkt der Erstellung festgelegt wurden und niemals geändert werden sollten. Oder ein berechneter Wert, der aus diesen Attributen abgeleitet wird. Ein gutes Beispiel ist die Java- StringKlasse.

  • Zuverlässigkeit und Contract - Objekte sind aus Primitiven zusammengesetzt ( int, char, double, usw.) und / oder anderen Objekten. Nicht alle Operationen, die auf diese Komponenten anwendbar sind, sollten anwendbar oder sogar logisch sein, wenn sie im größeren Objekt verwendet werden. Methoden mit dem finalModifikator können verwendet werden, um dies sicherzustellen. Die Counter-Klasse ist ein gutes Beispiel.


public class Counter {
    private int counter = 0;

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

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

Wenn die public final int count()Methode nicht ist final, können wir so etwas tun:

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

c.count(); // now count 2

Oder sowas:

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
quelle
27

Welche Art von "Vertrag" verspricht eine endgültige Methode?

Wenn Sie es anders sehen, garantiert jede nicht endgültige Methode implizit, dass Sie sie mit Ihrer eigenen Implementierung überschreiben können und die Klasse weiterhin wie erwartet funktioniert. Wenn Sie nicht garantieren können, dass Ihre Klasse das Überschreiben einer Methode unterstützt, sollten Sie sie endgültig machen.

josefx
quelle
Aber impliziert diese Ansicht nicht meine ursprüngliche Frage "Diese endgültige Methode wird sich immer wie versprochen verhalten", dass ich keine nicht endgültigen Methoden innerhalb einer endgültigen Methode aufrufen darf? Denn wenn ich das tue, wurde die aufgerufene Methode möglicherweise überschrieben und kann daher das Verhalten meiner endgültigen Methode nicht garantieren?
Towi
8

Zunächst können Sie nicht abstrakte Klassen finalsowie Felder und Methoden markieren . Auf diese Weise kann die gesamte Klasse nicht in Unterklassen unterteilt werden. Das Verhalten der Klasse wird also behoben.

Ich bin damit einverstanden, dass Markierungsmethoden finalnicht garantieren, dass ihr Verhalten in Unterklassen gleich ist, wenn diese Methoden nicht endgültige Methoden aufrufen. Wenn das Verhalten tatsächlich behoben werden muss, muss dies durch Konvention und sorgfältiges Design erreicht werden. Und vergessen Sie nicht, dies in Javadoc zu verstehen! (Java-Dokumentation)

Last but not least spielt das finalSchlüsselwort im Java Memory Model (JMM) eine sehr wichtige Rolle. JMM garantiert, dass finalSie keine ordnungsgemäße Synchronisierung benötigen , um die Sichtbarkeit von Feldern zu erreichen . Z.B:

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
quelle
Ja, ich kenne mich mit Abschlussklassen aus - einfacher Fall. Guter Punkt zu endgültigen Feldern mit JMM, keine Synchronisierung erforderlich ... hmm: Dies bezieht sich nur auf den "Zeiger", oder? Ich könnte das Objekt, auf das es verweist, immer noch nicht synchron ändern (ok, nicht in String, aber in benutzerdefinierten Klassen). Aber was Sie über "final garantiert das Verhalten nicht" gesagt haben, ist genau mein Punkt. Ich stimme zu, Doc und Design sind wichtig.
Towi
@towi Sie haben Recht, dass finaldie Sichtbarkeit von Änderungen, die an zusammengesetzten Objekten vorgenommen wurden, wie z Map.
Victor Sorokin
0

Ich bin mir nicht sicher, ob Sie Aussagen über die Verwendung von "final" machen können und wie sich dies auf den gesamten Designvertrag der Software auswirkt. Sie können sicher sein, dass kein Entwickler diese Methode überschreiben und seinen Vertrag auf diese Weise ungültig machen kann. Andererseits kann die endgültige Methode auf Klassen- oder Instanzvariablen beruhen, deren Werte durch Unterklassen festgelegt werden, und andere Klassenmethoden aufrufen, die überschrieben werden. Das Finale ist also höchstens eine sehr schwache Garantie.

Jim Ferrans
quelle
1
Ja das meinte ich. Richtig. Ich mag den Begriff "schwache Garantie" :-) Und ich mag (meistens) C ++ const. Wie in char const * const = "Hello"oder char const * const addName(char const * const name) const...
Towi
0

Nein, es liegt nicht außerhalb des Einflusses des Klassenautors. Sie können es in Ihrer abgeleiteten Klasse nicht überschreiben, daher wird es das tun, was der Autor der Basisklasse beabsichtigt hat.

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

Bemerkenswert ist der Teil, in dem vorgeschlagen wird, Methoden zu verwenden, die von Konstruktoren aufgerufen werden final.

Brian Roach
quelle
1
Nun, technisch wird es das tun, was der Autor der Basisklasse geschrieben hat .
Joey