Metodo `final` di Java: cosa promette?


141

In una classe Java è possibile definire un metodo finalper indicare che questo metodo non può essere ignorato:

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

È chiaro e può essere utile proteggere da sostituzioni accidentali o forse dalle prestazioni, ma non è questa la mia domanda.

La mia domanda è: da un punto di vista OOP ho capito che, definendo un metodo, finalil progettista di classe promette che questo metodo funzionerà sempre come descritto o implicito. Ma spesso questo può essere al di fuori dell'influenza dell'autore della classe, se ciò che il metodo sta facendo è più complicato della consegna di una proprietà .

Il vincolo sintattico mi è chiaro, ma qual è l'implicazione in senso OOP? È finalusato correttamente in questo senso dalla maggior parte degli autori di classe?

Che tipo di "contratto" finalpromette un metodo?

Risposte:


155

Come accennato, finalviene utilizzato con un metodo Java per contrassegnare che il metodo non può essere sovrascritto (per ambito di oggetti) o nascosto (per statico). Ciò consente allo sviluppatore originale di creare funzionalità che non possono essere modificate dalle sottoclassi e questa è tutta la garanzia che offre.

Ciò significa che se il metodo si basa su altri componenti personalizzabili come campi / metodi non pubblici, la funzionalità del metodo finale potrebbe essere comunque personalizzabile. Questo è buono anche se (con polimorfismo) consente una personalizzazione parziale.

Esistono diversi motivi per impedire che qualcosa sia personalizzabile, tra cui:

  • Prestazioni : alcuni compilatori possono analizzare e ottimizzare l'operazione, in particolare quella senza effetti collaterali.

  • Ottieni dati incapsulati : osserva gli oggetti immutabili in cui i loro attributi sono impostati al momento della costruzione e non devono mai essere modificati. O un valore calcolato derivato da tali attributi. Un buon esempio è la Stringclasse Java .

  • Affidabilità e Contract - Oggetti sono composti di primitive ( int, char, double, etc.) e / o altri oggetti. Non tutte le operazioni applicabili a tali componenti dovrebbero essere applicabili o persino logiche quando vengono utilizzate nell'oggetto più grande. I metodi con il finalmodificatore possono essere utilizzati per garantire ciò. La classe Counter è un buon esempio.


public class Counter {
    private int counter = 0;

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

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

Se il public final int count()metodo non lo è final, possiamo fare qualcosa del genere:

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

c.count(); // now count 2

O qualcosa del genere:

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

27

Che tipo di "contratto" promette un metodo finale?

Guardalo dall'altra parte, qualsiasi metodo non definitivo offre la garanzia implicita che puoi sovrascriverlo con la tua implementazione e la classe continuerà a funzionare come previsto. Quando non puoi garantire che la tua classe supporti la sovrascrittura di un metodo, dovresti renderlo definitivo.


Ma questo punto di vista non implica la mia domanda originale "questo metodo finale si comporterà sempre come promesso" che non devo chiamare alcun metodo non finale all'interno di un metodo finale? Perché, se lo faccio, il metodo chiamato potrebbe essere stato ignorato e quindi non posso garantire il comportamento del mio metodo finale?
towi,

8

Prima di tutto, puoi contrassegnare classi non astratte, finalnonché campi e metodi. In questo modo l'intera classe non può essere sottoclassata. Quindi, il comportamento della classe verrà risolto.

Concordo sul fatto che i metodi di marcatura finalnon garantiscono che il loro comportamento sarà lo stesso nelle sottoclassi se questi metodi chiamano metodi non finali. Se il comportamento deve davvero essere risolto, questo deve essere raggiunto per convenzione e con un design accurato. E non dimenticare di pensarlo in javadoc! (Documentazione java)

Ultimo ma non meno importante, la finalparola chiave ha un ruolo molto importante in Java Memory Model (JMM). È garantito da JMM che per ottenere visibilità dei finalcampi non è necessaria una corretta sincronizzazione. Per esempio:

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);
  }
}  

Sì, conosco le lezioni finali - caso semplice. Un buon punto sui campi finali con JMM, nessuna sincronizzazione necessaria ... hmm: questo si riferisce solo al "puntatore", giusto? Potrei ancora modificare fuori sincrono l'oggetto a cui si riferisce (ok, non in String, ma in classi definite dall'utente). Ma quello che hai detto su "final non garantisce il comportamento" è esattamente il mio punto. Sono d'accordo, doc e design sono importanti.
towi,

Hai ragione @towi che finalnon assicurerà la visibilità delle modifiche apportate a oggetti composti, come ad esempio Map.
Victor Sorokin,

0

Non sono sicuro che tu possa fare affermazioni sull'uso di "final" e su come ciò influisca sul contratto di progettazione generale del software. Si garantisce che nessuno sviluppatore può ignorare questo metodo e annullare il suo contratto in questo modo. D'altra parte, il metodo finale può fare affidamento su variabili di classe o di istanza i cui valori sono impostati da sottoclassi e può chiamare altri metodi di classe che sono sovrascritti. Quindi il finale è al massimo una garanzia molto debole.


1
Sì, questo è quello che intendevo. Destra. Mi piace il termine "garanzia debole" :-) E mi piace (soprattutto) il C ++ const. Come in char const * const = "Hello"o char const * const addName(char const * const name) const...
towi,

0

No, non è al di fuori dell'influenza dell'autore della classe. Non puoi sovrascriverlo nella tua classe derivata, quindi farà ciò che intendeva l'autore della classe base.

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

Da notare la parte in cui suggerisce che dovrebbero essere i metodi chiamati dai costruttori final.


1
Bene, tecnicamente farà ciò che ha scritto l' autore della classe base .
Joey,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.