Immutabilità orientata agli oggetti: classi finali o metodi finali

3

Una delle cose che vedi in molti posti nella libreria java standard sono le classi finali. Si sostiene che questo è per immutabilità che capisco ... in parte.

Supponi di avere una classe:

final public class ImmutableTest {
    private String value;
    public ImmutableTest(String value) {
        this.value = value;
    }
    public String getValue() {
        return this.value;
    }
}

Si può dire che il valore è immutabile perché non può essere adattato dopo la creazione. Tuttavia, ciò limita l'usabilità della classe in quanto nessuno può estenderla (ad esempio, può essere utilizzata come drop-in per un design male interfacciato ...).

Ora supponi di avere questa classe:

public class ImmutableTest {
    private String value;
    public ImmutableTest(String value) {
        this.value = value;
    }
    final public String getValue() {
        return this.value;
    }
}

Il "valore" è ancora immutabile, ma ora almeno le persone possono estendere la classe. Qualcuno una volta si è interposto: ma se lo estendi, la classe estesa non è garantita per essere immutabile!

Anche se questo è vero, penso che sia irrilevante per la classe stessa.

Se l'utente A usa la mia classe ImmutableTest ovunque nel suo codice, ha comunque accesso al "valore" comunque che rimane immutabile . A meno che non faccia un casting esplicito (in altre parole: è consapevole che sta tentando di accedere ad altre cose) non può accedere a una parte "mutabile" dell'istanza reale, il che significa che dal punto di vista dello sviluppatore la classe è immutabile come dovrebbe essere, anche se l'istanza attuale ha altre variabili modificabili.

In breve: la mia opinione è che le lezioni finali sono superflue in quanto i metodi finali sono tutto ciò che serve. Mi piacerebbe comunque sentire argomenti contrari!

Aggiorna

Per chiarire: non sto affermando che aggiungere "finale" a una classe o ai suoi metodi lo rende immutabile.

Supponiamo di avere una classe progettata per essere immutabile, ma non si rendono i metodi o la finale di classe, una sottoclasse può ovviamente renderla nuovamente mutabile. Questo argomento viene utilizzato per spiegare perché alcune classi nella libreria standard sono finali. La mia opinione è che avrebbe dovuto essere sufficiente per finalizzare i metodi di accesso alla parte immutabile e lasciare la classe non definitiva.

Questo potrebbe lasciare la finestra aperta agli oggetti secondari aggiungendo i campi mutabili ma l'insieme originale di campi rimarrebbe immutabile, soddisfacendo quindi il requisito "immutabile" per l'insieme iniziale di metodi.

UPDATE2

La conclusione è che le classi finali non hanno uno scopo reale tranne forse una brevità che di per sé non è una ragione sufficiente per perdere l'estensibilità secondo me.

    
posta nablex 15.10.2013 - 13:22
fonte

3 risposte

9

Dichiarare la classe immutabile final salva il programmatore dalla necessità di ripetere la dichiarazione finale in ogni singola dichiarazione di metodo, ancora e ancora e ancora.

In classi come java.lang.String , avendo oltre 60 metodi, questo è un notevole risparmio, oltre a garantire che il modificatore necessario non venga omesso per errore.

Quando l'oggetto è immutabile, è difficile individuare errori nel dichiarare il metodo finale, perché non esiste un modo affidabile per dire se il programmatore ha omesso intenzionalmente o per errore il modificatore finale.

final classes have no real purpose except perhaps brevity

Oltre alla brevità e al tentativo di evitare errori, un strong vantaggio di dichiarare un finale di classe immutabile è che questo rende l'emendamento esplicito del programmatore, comunicando in modo inequivocabile agli utenti API che nessuno dei metodi di classe è destinato alla sovrascrittura.

Il modo alternativo, ovvero dichiarare tutti i metodi finali, manca di questa importante "funzionalità", in quanto lascia gli utenti dell'API nello stato indeciso, indipendentemente dal fatto che i progettisti dell'API intendessero solo proteggere alcuni metodi dall'overloading, e solo accidentalmente è risultato che tutti loro sono diventati definitivi o che è stata presa una decisione di progettazione per coprire tutta la classe.

my opinion is that final classes are superfluous as final methods are all you need

Dato sopra, avere un modificatore finale per la classe non mi sembra superfluo.

Vale la pena notare che tutorial Sun / Oracle Java presenta le classi finali come questione di utilità e non come quella di convenienza. Se lo scopo delle classi finali fosse semplice brevità / zucchero sintattico, ci si aspetterebbe che il tutorial spieghi questi come convenienza.

...you can also declare an entire class final. A class that is declared final cannot be subclassed. This is particularly useful, for example, when creating an immutable class like the String class.

    
risposta data 15.10.2013 - 14:23
fonte
6

Le classi non sono mutabili o immutabili, la mutabilità di solito si riferisce allo stato degli oggetti in fase di runtime.

Classi finali, metodi e membri hanno obiettivi diversi.

Una classe finale non può essere estesa da altre classi. A seconda del progetto, questa può essere una proprietà progettuale desiderabile di questa classe. Ciò non significa che tutti i membri di questa classe sono immutabili per impostazione predefinita! L'immutabilità logica può essere un effetto collaterale come nel tuo esempio. Se una classe è definitiva e tutti i suoi campi sono uguali, l'oggetto è strongmente immutabile .

I metodi finali indicano che il metodo non può essere sovrascritto in una sottoclasse. Questo può essere utile, ad esempio, con il modello di metodo del modello in cui non si desidera che i client eseguano l'override del metodo del modello anziché degli hook. Questo, ancora, non ha nulla a che fare con lo stato immutabile perché non si riferisce direttamente a un membro che è immutabile.

I campi finali sono dove l'immutabilità entra davvero in gioco. Se hai posizionato la parola chiave finale davanti a value come private final String value; , assicurati che un valore non possa essere ignorato, indipendentemente da chi ha esteso cosa.

    
risposta data 15.10.2013 - 13:44
fonte
0

Lo stato è definito dai campi di una classe e quindi renderli immutabili (usando finale e privato) dovrebbe funzionare per variabili di tipo primitive. Ma nel caso di variabili che contengono un riferimento ad un altro oggetto, per esempio x, anche questa x dovrebbe essere immutabile. Mentre pubblichiamo la x da getter, dobbiamo assicurarci di non compromettere l'immutabilità di x. Ad esempio:

class A
{
final int i;

final HashMap<int, B> map;

public A(int i,Map map){
this.i=i;
this.map=map;
}
public int getI(){
return i;
}
public int getJ(int index){
return map.get(index).getJ();
}

class B{
 int j;
public int getJ()
{
return j;
}

public void setJ(int j)
{
this.j=j;
}
}

....

Da sopra lo snippet di codice, la mappa è immutabile dal momento che è definitiva e la mappa non viene mai pubblicata nel mondo esterno.

Ma cosa succede se qualche altra classe dice C, estende A e poi sovrascrive il metodo getJ (int index)?

Per impedirlo, dobbiamo dichiarare il metodo getJ (int index) final. Ora ci possono essere molti di questi metodi in una classe e controllare se ognuno di essi dovrebbe essere definitivo può essere soggetto a errori. Quindi dichiariamo la finale di classe e lasciamo che altri sappiano che nessuno dei suoi metodi e variabili sono limitati a cambiare e quindi ignorarlo non è possibile.

Possiamo dichiarare sia la mappa sia privata e finale, quindi non è accessibile alle classi figlie, ma di nuovo ci possono essere altri campi / variabili mutabili in A o B che possono essere usati e pubblicati dalla classe di bambini C     

risposta data 11.04.2018 - 10:09
fonte

Leggi altre domande sui tag