2010-08-24 17 views
20

Utilizzo PMD per individuare potenziali problemi nel mio codice Java e ho trovato il suo consiglio da dividere tra l'utile, l'idiosincratico e il "WTF ?!".Tutto Finale

Una delle cose che continua a dirmi è usare la parola chiave final per letteralmente ogni variabile a cui posso collegarlo, inclusi i parametri di input. Per le costanti attuali questo sembra ragionevole, ma per altre cose mi sembra strano, forse anche un po 'controproducente.

Ci sono vantaggi/svantaggi concreti nell'impiccagione di final su ogni dichiarazione di variabili possibilmente possibile?

+1

Vedi http://stackoverflow.com/questions/137868/using-final-modifier-whenever-applicable-in-java –

risposta

21

"Ogni dichiarazione delle variabili possibilmente possibile" suona un po 'estremo, ma final è effettivamente vantaggioso in molti modi. A volte vorrei che final fosse il comportamento predefinito e non richiedesse alcuna parola chiave, ma le vere "variabili" richiedevano un modificatore variable. Scala ha adottato un approccio simile a questo approccio con le sue val e var parole chiave — utilizzando val (la parola chiave final -like) è fortemente incoraggiata.

È particolarmente importante considerare attentamente se ciascuna variabile membro è final, volatile o nessuna, poiché la sicurezza del thread della classe dipende dall'ottenere questo diritto. I valori assegnati alle variabili final e volatile sono sempre visibili ad altri thread, senza utilizzare un blocco synchronized.

Per le variabili locali, non è così importante, ma l'utilizzo di final può aiutare a ragionare sul codice in modo più chiaro ed evitare alcuni errori. Se non ti aspetti che un valore cambi all'interno di un metodo, dillo con final e lascia che il compilatore trovi le violazioni inosservate di questa aspettativa.Al momento non sono a conoscenza di alcunché, ma è facilmente concepibile che un compilatore JIT possa utilizzare questo suggerimento per migliorare anche le prestazioni.

In pratica, non dichiaro le variabili locali final in qualsiasi momento. Non mi piace l'ingombro visivo e sembra ingombrante. Ma questo non significa che non sia qualcosa che io dovrei fare do.

Una proposta è stata fatta per aggiungere la parola chiave var a Java finalizzato a supportare l'inferenza dei tipi. Ma come parte di questa proposta, ci sono stati un certo numero di suggerimenti per ulteriori modi di specificare l'immutabilità delle variabili locali. Ad esempio, un suggerimento era di aggiungere anche la parola chiave val per dichiarare una variabile immutabile con tipo inferito. In alternativa, alcuni sostengono usando final e var insieme.

+0

Visto a posteriori che sarebbe stato il migliore. Avrebbe, tuttavia, rotto la semantica C, che era esplicitamente richiesta per dare ai programmatori C++ una transizione facile. –

2

Questo è un linguaggio comune per strumenti come PMD. Ad esempio, di seguito sono riportate le regole corrispondenti in Checkstyle. È davvero una questione di stile/preferenza e potresti discutere per entrambe le parti.

A mio parere, l'utilizzo di final per i parametri del metodo e le variabili locali (quando applicabile) è di buon livello. L'idioma del "design per l'estensione" è discutibile.

5

finale dice al lettore che il valore o riferimento assegnato prima è lo stesso in qualsiasi momento.

Come tutto ciò che può essere definitiva è definitiva in questo scenario, un mancante finale dice al lettore che il valore sarà cambiamento in seguito, e per tenerne conto.

+0

che ha senso per primitive, o per oggetti immutabili come String. Ma questo incoraggia un falso senso di sicurezza per oggetti mutabili? – BlairHippo

+0

Una finale mancante ti dice solo che può cambiare in seguito, non certo che lo farà. Assumere che sia semplicemente sbagliato. Se una classe non è definita come definitiva, può essere sottoclasse, non significa che sia. L'uso del finale per tutte le variabili è aperto al dibattito (qui qui), quindi la sua assenza può essere facilmente attribuita alle preferenze del coder. – Robin

+1

@Robin, si ricorda che l'OP ha dichiarato "Tutto è definitivo". Questo è il caso che sto discutendo. Si prega di leggere correttamente le domande prima di fare downvoting delle risposte. –

1

PMD ha anche regole di opzione che è possibile attivare che si lamenta su final; è una regola arbitraria.

Se sto facendo un progetto in cui l'API viene esportata in un'altra squadra o nel mondo, lasciare la regola PMD così com'è. Se stai solo sviluppando qualcosa che sarà per sempre e sempre un'API chiusa, disabilita la regola e risparmia tempo.

1

Ecco alcuni dei motivi per cui può essere benefitial di aver quasi tutto contrassegnati comefinal

finale Costanti

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 poi per altre parti dei vostri codici, o accessibile da altre classi, in questo modo se cambieresti il ​​valore non dovresti cambiarlo 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 costruiscono una variabile finale ambito che aggiunge un prefisso al parametro environmentKey. In questo caso, la variabile finale è valida solo nell'ambito di esecuzione, che è differente ad ogni esecuzione del metodo. Ogni volta che si inserisce il metodo, la finale viene ricostruita. Non appena viene creato, non può essere modificato durante l'esecuzione del metodo. Questo permette di fissare 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"); 
    } 
} 

finale Costanti

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 si hanno veramente lunghe linee di codici, e genererà errore del compilatore in modo da non correre per logica/errore di business quando qualcuno cambia accidentalmente variabili che non devono essere modificate.

Collezioni finali

caso diverso quando stiamo parlando Collezioni, è necessario impostare loro come un immodificabile.

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 si imposta come immodificabile:

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

classi finali e metodi finali non può essere esteso o sovrascritti, rispettivamente.

EDIT: PER AFFRONTARE LA CLASSE PROBLEMA finale per quanto riguarda INCAPSULAMENTO:

Ci sono due modi per fare un finale di classe. Il primo è quello di utilizzare la parola chiave final nella dichiarazione della classe:

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

Il secondo modo per fare una classe finale è di dichiarare tutti i suoi costruttori come privato:

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

marcatura finale puoi risparmiare il guaio se si scopre che è effettivamente una finale, per dare un'occhiata a 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 estendere questa classe. 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 possono causare problemi.

Quindi è necessario contrassegnarlo come finale quando si effettua implicitamente un finale di classe rendendolo privato del costruttore.