Quando si dovrebbe usare final per i parametri del metodo e le variabili locali?

StackOverflow https://stackoverflow.com/questions/154314

  •  03-07-2019
  •  | 
  •  

Domanda

Ho trovato un paio di riferimenti ( per esempio ) che suggeriscono di utilizzare final il più possibile e mi chiedo quanto sia importante. Ciò è principalmente nel contesto dei parametri del metodo e delle variabili locali, non dei metodi o delle classi finali. Per le costanti, ha ovviamente senso.

Da un lato, il compilatore può apportare alcune ottimizzazioni e rende più chiara l'intenzione del programmatore. D'altra parte, aggiunge verbosità e le ottimizzazioni possono essere banali.

È qualcosa che dovrei fare uno sforzo per ricordare?

È stato utile?

Soluzione

Obsess over:

  • Campi finali: contrassegnare i campi come finali li costringe a essere impostati entro la fine della costruzione, rendendo immutabile quel riferimento di campo. Ciò consente la pubblicazione sicura dei campi e può evitare la necessità di sincronizzazione su letture successive. (Nota che per un riferimento ad oggetto, solo il riferimento al campo è immutabile - le cose a cui fa riferimento il riferimento all'oggetto possono ancora cambiare e ciò influisce sull'immutabilità.)
  • Campi statici finali - Anche se ora uso enum per molti dei casi in cui utilizzavo campi finali statici.

Considera ma usa giudiziosamente:

  • Classi finali - Il design del framework / API è l'unico caso in cui lo considero.
  • Metodi finali: praticamente uguale alle classi finali. Se stai usando modelli di metodi come folli e segnando elementi finali, probabilmente stai facendo troppo affidamento sull'eredità e non abbastanza sulla delega.

Ignora se non ti senti anale:

  • Parametri del metodo e variabili locali - RARAMENTE lo faccio principalmente perché sono pigro e trovo che ingombra il codice. Devo ammettere pienamente che la marcatura dei parametri e delle variabili locali che non ho intenzione di modificare è "più giusta". Vorrei che fosse l'impostazione predefinita. Ma non lo è e trovo il codice più difficile da capire con le finali dappertutto. Se sono nel codice di qualcun altro, non lo estrarrò, ma se scrivo un nuovo codice non lo inserirò. Un'eccezione è il caso in cui devi contrassegnare qualcosa di definitivo in modo da poter accedere dall'interno di una classe interna anonima.

Altri suggerimenti

  

È qualcosa che dovrei fare uno sforzo per ricordarmi di fare?

No, se si utilizza Eclipse, poiché è possibile configurare un'azione di salvataggio per aggiungere automaticamente questi modificatori finali . Quindi ottieni i vantaggi con meno sforzo.

I vantaggi in tempo di sviluppo di " final " sono almeno significativi quanto i benefici di runtime. Indica ai futuri editor del codice qualcosa sulle tue intenzioni.

Contrassegnare una classe "finale" indica che durante la progettazione o l'implementazione della classe non hai compiuto sforzi per gestire l'estensione in modo corretto. Se i lettori possono apportare modifiche alla classe e desiderano rimuovere la "quotazione" finale modificatore, possono farlo a proprio rischio. Sta a loro assicurarsi che la classe gestisca bene l'estensione.

Contrassegnare una variabile "finale" (e assegnandolo nel costruttore) è utile con l'iniezione di dipendenza. Indica il "collaboratore" natura della variabile.

Contrassegno di un metodo "finale" è utile in classi astratte. Delinea chiaramente dove si trovano i punti di estensione.

Ho trovato che i parametri del metodo di marcatura e i locali come final sono utili come ausilio per il refactoring quando il metodo in questione è un pasticcio incomprensibile lungo diverse pagine. Cospargi final liberamente, vedi cosa "non può assegnare alla variabile finale" errori generati dal compilatore (o dal tuo IDE) e potresti scoprire perché la variabile chiamata "quotazioni" finisce null anche se molti commenti (non aggiornati) giurano che non possono accadere.

Quindi puoi correggere alcuni degli errori sostituendo le variabili riutilizzate con nuove variabili dichiarate più vicine al punto di utilizzo. Quindi scopri che puoi racchiudere intere parti del metodo tra parentesi graffe e all'improvviso sei a un tasto IDE lontano da "Metodo di estrazione" e il tuo mostro è appena diventato più comprensibile.

Se il tuo metodo non è non già un disastro non sostenibile, suppongo che potrebbe essere utile rendere le cose definitive per scoraggiare le persone dal trasformarle in detto disastro; ma se è un metodo breve (vedi: non non mantenibile), rischi di aggiungere molta verbosità. In particolare, le firme delle funzioni Java sono abbastanza difficili da contenere 80 caratteri, così come è senza aggiungere altri sei per argomento!

Uso sempre final per rendere Java più basato sulle espressioni. Vedi le condizioni di Java ( if, else, switch ) non sono basate sull'espressione che ho sempre odiato soprattutto se sei abituato alla programmazione funzionale (es. ML, Scala o Lisp).

Quindi dovresti provare a usare sempre (IMHO) le variabili finali quando usi le condizioni.

Lascia che ti faccia un esempio:

    final String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            break;
        case JOB_POSTING_IMPORT:
            name = "Blah";
            break;
        default:
            throw new IllegalStateException();
    }

Ora Se aggiungi un'altra istruzione case e non imposti name il compilatore fallirà. Il compilatore fallirà anche se non si interrompe ogni caso (che si imposta la variabile). Ciò ti consente di rendere Java molto simile alle espressioni let di Lisp e di renderlo così non indentato in modo massiccio dal tuo codice (a causa delle variabili di ambito lessicale).

E come notato da @Recurse (ma apparentemente -1 me) puoi fare il precedente senza creare String name final per ottenere l'errore del compilatore (che non ho mai detto non è stato possibile) ma potresti facilmente far scomparire l'errore del compilatore impostando il nome dopo l'istruzione switch che elimina la semantica dell'espressione o peggio dimenticando di break che non puoi causare un errore (nonostante ciò che @Recurse dice) senza usare final :

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            //break; whoops forgot break.. 
            //this will cause a compile error for final ;P @Recurse
        case JOB_POSTING_IMPORT:
            name = "Blah";
            break;
    }
    // code, code, code
    // Below is not possible with final
    name = "Whoops bug";

A causa del nome dell'impostazione del bug (oltre a dimenticare break che è anche un altro bug) ora posso accidentalmente fare questo:

    String name;
    switch(pluginType) {
        case CANDIDATE_EXPORT:
            name = "Candidate Stuff";
            break;
        //should have handled all the cases for pluginType
    }
    // code, code, code
    // Below is not possible with final
    name = "Whoops bug";

La variabile finale forza una singola valutazione di quale nome dovrebbe essere. Simile a come una funzione che ha un valore di ritorno deve sempre restituire un valore (ignorando le eccezioni), il blocco interruttore nome dovrà risolvere il nome e quindi legato a quel blocco interruttore che rende più semplice il refactoring di blocchi di codice (ovvero il refactor Eclipe: metodo di estrazione) .

Quanto sopra in OCaml:

type plugin = CandidateExport | JobPostingImport

let p = CandidateExport

let name = match p with
    | CandidateExport -> "Candidate Stuff"
    | JobPostingImport -> "Blah" ;;

La corrispondenza ... con ... viene valutata come una funzione, ad esempio un'espressione. Nota come appare la nostra istruzione switch.

Ecco un esempio in Scheme (Racket o Chicken):

(define name 
    (match b
      ['CandidateExport "Candidate Stuff"]
      ['JobPostingImport "Blah"]))

Bene, tutto dipende dal tuo stile ... se ti piace vedere la finale quando non modificherai la variabile, allora usala. Se NON TI PIACE vederlo ... allora lascialo fuori.

Personalmente mi piace la minore verbosità possibile, quindi tendo ad evitare l'uso di parole chiave extra che non sono realmente necessarie.

Preferisco i linguaggi dinamici, quindi probabilmente non è una sorpresa che mi piaccia evitare la verbosità.

Quindi, direi solo scegliere la direzione verso la quale ti stai inclinando e seguirla (in ogni caso, cerca di essere coerente).


Come nota a margine, ho lavorato su progetti che utilizzano entrambi e non usano tale schema, e non ho visto alcuna differenza nella quantità di bug o errori ... Non penso che sia uno schema che migliorerà enormemente il conteggio dei bug o altro, ma di nuovo è stile e se ti piace esprimere l'intenzione di non modificarlo, vai avanti e usalo.

È utile nei parametri evitare di modificare accidentalmente il valore del parametro e introdurre un bug sottile. Uso per ignorare questa raccomandazione ma dopo aver trascorso circa 4 ore. in un metodo orribile (con centinaia di righe di codice e più fors, if nidificati e ogni tipo di cattiva pratica) ti consiglierei di farlo.

 public int processSomethingCritical( final int x, final int y ){
 // hundreds of lines here 
     // for loop here...
         int x2 = 0;
        x++; // bug aarrgg...
 // hundreds of lines there
 // if( x == 0 ) { ...

 }

Ovviamente in un mondo perfetto questo non accadrà, ma ... beh ... a volte devi supportare il codice degli altri. :(

Se stai scrivendo un'applicazione in cui qualcuno dovrà leggere il codice dopo, diciamo, 1 anno, quindi sì, usa la variabile finale su che non deve essere modificata in ogni momento. In questo modo, il tuo codice sarà più "auto-documentante" e riduci anche la possibilità per gli altri sviluppatori di fare cose stupide come usare una costante locale come variabile temporanea locale.

Se stai scrivendo del codice usa e getta, allora, nah, non preoccuparti di identificare tutte le costanti e renderle definitive.

Userò final il più possibile. In questo modo verrà contrassegnato se si modifica involontariamente il campo. Ho anche impostato i parametri del metodo su final. In questo modo ho rilevato diversi bug dal codice che ho rilevato quando provano a "impostare" un parametro dimenticando i passaggi Java per valore.

Non è chiaro dalla domanda se questo sia ovvio, ma rendere definitivo un parametro del metodo influenza solo il corpo del metodo. NON trasmette informazioni interessanti sulle intenzioni del metodo all'invocatore. L'oggetto che viene passato può ancora essere mutato all'interno del metodo (le finali non sono cons) e l'ambito della variabile rientra nel metodo.

Per rispondere alla tua precisa domanda, non mi preoccuperei di creare un'istanza o una variabile locale (inclusi i parametri del metodo) final a meno che il codice non lo richiedesse (ad esempio la variabile sia referenziata da una classe interna), o per chiarire una logica davvero complicata .

Ad esempio le variabili, le renderei definitive se sono logicamente costanti.

Vi sono molti usi per la variabile final . Eccone alcuni

Costanti finali

 public static class CircleToolsBetter {
     public final static double PI = 3.141;
        public double getCircleArea(final double radius) {
          return (Math.pow(radius, 2) * PI);
        }
    }

Questo può essere usato quindi per altre parti dei tuoi codici, o accessibile da altre classi, in questo modo se dovessi mai cambiare il valore non dovresti cambiarli uno per uno.

Variabili finali

public static String someMethod(final String environmentKey) {
    final String key = "env." + environmentKey;
    System.out.println("Key is: " + key);
    return (System.getProperty(key));

  }

}

In questa classe, si crea una variabile finale con ambito che aggiunge un prefisso al parametro environmentKey. In questo caso, la variabile finale è finale solo nell'ambito di esecuzione, che è diversa ad ogni esecuzione del metodo. Ogni volta che si inserisce il metodo, il finale viene ricostruito. Non appena viene costruito, non può essere modificato durante l'ambito dell'esecuzione del metodo. Ciò consente di correggere una variabile in un metodo per la durata del metodo. vedi sotto:

public class FinalVariables {


  public final static void main(final String[] args) {
    System.out.println("Note how the key variable is changed.");
    someMethod("JAVA_HOME");
    someMethod("ANT_HOME");
  }
}

Costanti finali

public double equation2Better(final double inputValue) {
    final double K = 1.414;
    final double X = 45.0;

double result = (((Math.pow(inputValue, 3.0d) * K) + X) * M);
double powInputValue = 0;         
if (result > 360) {
  powInputValue = X * Math.sin(result); 
} else {
  inputValue = K * Math.sin(result);   // <= Compiler error   
}

Questi sono particolarmente utili quando hai lunghe righe di codice e genereranno errori del compilatore in modo da non incorrere in errori di logica / business quando qualcuno modifica accidentalmente variabili che non dovrebbero essere modificate.

Collezioni finali

Caso diverso quando parliamo di Collezioni, è necessario impostarle come non modificabili.

 public final static Set VALID_COLORS; 
    static {
      Set temp = new HashSet( );
      temp.add(Color.red);
      temp.add(Color.orange);
      temp.add(Color.yellow);
      temp.add(Color.green);
      temp.add(Color.blue);
      temp.add(Color.decode("#4B0082")); // indigo
      temp.add(Color.decode("#8A2BE2")); // violet
      VALID_COLORS = Collections.unmodifiableSet(temp);
    }

altrimenti, se non lo si imposta come non modificabile:

Set colors = Rainbow.VALID_COLORS;
colors.add(Color.black); // <= logic error but allowed by compiler

Classi finali e Metodi finali non possono essere estesi o sovrascritti rispettivamente.

MODIFICA: PER INDIRIZZARE IL PROBLEMA DELLA CLASSE FINALE PER QUANTO RIGUARDA L'ECCAPSULAZIONE:

Esistono due modi per rendere finale una classe. Il primo è usare la parola chiave final nella dichiarazione di classe:

public final class SomeClass {
  //  . . . Class contents
}

Il secondo modo per rendere finale una classe è dichiarare privati ??tutti i suoi costruttori:

public class SomeClass {
  public final static SOME_INSTANCE = new SomeClass(5);
  private SomeClass(final int value) {
  }

Contrassegnarlo come finale consente di risparmiare il problema se si scopre che è un finale, per dimostrare l'aspetto di questa classe di test. sembra pubblico a prima vista.

public class Test{
  private Test(Class beanClass, Class stopClass, int flags)
    throws Exception{
    //  . . . snip . . . 
  }
}

Sfortunatamente, poiché l'unico costruttore della classe è privato, è impossibile estenderla. Nel caso della classe Test, non vi è alcun motivo per cui la classe debba essere definitiva. La classe Test è un buon esempio di come le classi finali implicite possano causare problemi.

Quindi dovresti contrassegnarlo come finale quando rendi implicitamente una classe finale rendendola privata come costruttore.

Un po 'un compromesso, come dici tu, ma preferisco l'uso esplicito di qualcosa piuttosto che un uso implicito. Ciò contribuirà a rimuovere alcune ambiguità per i futuri manutentori del codice, anche se sei solo tu.

Se si dispone di classi interne (anonime) e il metodo deve accedere alla variabile del metodo contenitore, è necessario disporre di tale variabile come finale.

A parte questo, quello che hai detto è giusto.

Usa la parola chiave final per una variabile se la stai impostando come immutable

Dichiarando la variabile come definitiva, aiuta gli sviluppatori a escludere possibili problemi di modifica delle variabili in un ambiente altamente multi-thread.

Con la versione java 8, abbiamo un altro concetto chiamato " variabile finale effettivamente " ;. Una variabile non finale può apparire come variabile finale.

le variabili locali a cui fa riferimento un'espressione lambda devono essere definitive o effettivamente definitive

  

Una variabile viene considerata finale efficace se non viene modificata dopo l'inizializzazione nel blocco locale. Ciò significa che ora puoi utilizzare la variabile locale senza la parola chiave finale all'interno di una classe anonima o espressione lambda, a condizione che debbano essere effettivamente finali.

Fino a Java 7, non è possibile utilizzare una variabile locale non finale all'interno di una classe anonima, ma da Java 8 è possibile

Dai un'occhiata a questo articolo

Una risposta molto semplice abbiamo 3 casi con Final con variabili, Final con metodi & amp; & amp; Finale con lezioni ..

1.Final con variabile: non puoi assegnare questa variabile più di una volta ..

2.Final con metodi: non puoi ignorare questo metodo ..

3.Fine con le classi: non puoi estendere nessuna classe finale

Prima di tutto, la parola chiave finale viene utilizzata per creare una costante variabile. Costante significa che non cambia. Ad esempio:

final int CM_PER_INCH = 2.54;

Dichiareresti la variabile final perché non cambia un centimetro per pollice.

Se si tenta di sovrascrivere un valore finale, la variabile è quella che è stata dichiarata per prima. Ad esempio:

final String helloworld = "Hello World";
helloworld = "A String"; //helloworld still equals "Hello World"

C'è un errore di compilazione simile a:

local variable is accessed from inner class, must be declared final

Se la tua variabile non può essere dichiarata final o se non vuoi dichiararla final prova questo:

final String[] helloworld = new String[1];
helloworld[0] = "Hello World!";
System.out.println(helloworld[0]);
helloworld[0] = "A String";
System.out.println(helloworld[0]);

Questo stamperà:

Hello World!
A String
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top