Chi può toccare il tuo codice
Un modificatore di accesso è una parola chiave davanti a una classe, un campo, un metodo o un costruttore che decide chi altro può usarlo. È la base dell'incapsulamento: nascondi i dettagli interni di una classe ed esponi solo le parti che ti fidi a far chiamare ad altro codice.
Java ha quattro livelli, dal più aperto al più chiuso: public, protected, default (nessuna parola chiave) e private. Se li scegli bene, una classe diventa sicura da modificare in futuro, perché niente al di fuori dipende da dettagli che non hai mai promesso di mantenere.
private: visibile solo dentro la classe
private è il livello più stretto. Un membro private può essere toccato solo dal codice scritto nella stessa classe: non dalle sottoclassi, non da altre classi dello stesso package, da nessuno. È qui che dovrebbe stare la maggior parte dei tuoi campi.
balance è private, quindi l'unico modo per cambiarlo è passare da deposit, che rifiuta gli importi negativi. Se balance fosse public, qualsiasi codice potrebbe scrivere account.balance = -9999 e saltare del tutto il controllo. Quel controllo è proprio il motivo per cui si nasconde il campo.
Prova ad aggiungere account.balance = 500; in main e otterrai un errore di compilazione: balance has private access in BankAccount.
public: visibile ovunque
public è l'estremo opposto: chiunque, ovunque, può usare il membro. I metodi che altro codice deve chiamare sono di solito public. Lo è anche la classe stessa quando deve essere raggiungibile da altri package.
Lo schema comune, a volte chiamato API pubblica di una classe, è campi private, metodi public. I campi contengono uno stato che nessun altro dovrebbe toccare; i metodi sono le porte controllate per entrare e uscire. getBalance e deposit qui sopra sono esattamente questo.
default (package-private): nessuna parola chiave
Se non scrivi nessun modificatore, ottieni l'accesso default, detto anche package-private. Il membro è visibile a tutte le classi dello stesso package e invisibile a tutto ciò che sta fuori. Non esiste una parola chiave per questo livello: la sua assenza è il livello.
class Invoice { // nessun modificatore -> visibile solo in questo package
int amount; // nessun modificatore -> campo package-private
}
Qui cascano molti principianti: omettere public non rende qualcosa private, lo rende package-private. Un campo che volevi tenere segreto resta leggibile e scrivibile da qualsiasi classe che si trovi nello stesso package. Se vuoi nasconderlo davvero, devi scrivere private in modo esplicito.
L'accesso default è davvero utile per classi di supporto che sono un dettaglio implementativo di un package: non le esporti, ma le classi che collaborano all'interno del package possono comunque usarle liberamente.
protected: package più sottoclassi
protected è un gradino più permissivo di default. Un membro protected è visibile ovunque lo sia default (lo stesso package) e in più alle sottoclassi, anche a quelle in un altro package. È pensato per ciò che vuoi condividere con le classi che estendono la tua, ma non con tutto il mondo.
Dog accede a name e sound() perché sono protected e Dog è una sottoclasse. Il codice esterno che non è una sottoclasse e non sta nel package continua a non poterli toccare. Usa protected quando progetti una classe pensata per essere estesa; altrimenti preferisci private.
I quattro livelli a colpo d'occhio
Ecco il quadro completo, dal più restrittivo al più aperto:
| Modificatore | Stessa classe | Stesso package | Sottoclasse (altro package) | Ovunque |
|---|---|---|---|---|
private | sì | no | no | no |
| default | sì | sì | no | no |
protected | sì | sì | sì | no |
public | sì | sì | sì | sì |
Una regola pratica semplice: parti da private e allarga solo quando qualcosa ha davvero bisogno di un accesso più ampio. È molto più facile allargare l'accesso in seguito che restringerlo quando altro codice ha già iniziato a dipendere da un membro.
Modificatori sulle classi
I modificatori di accesso si applicano anche alle classi, non solo ai membri, ma una classe top-level (cioè non annidata in un'altra) può essere solo public o default. Non puoi scrivere una classe top-level private o protected.
public class Order { ... } // raggiungibile da qualsiasi package
class LineItem { ... } // default: solo questo package può vederla
Una classe top-level public deve stare in un file che porta il suo nome (Order.java). Le classi annidate, invece, possono usare tutti e quattro i modificatori: ecco perché gli esempi qui sopra potevano marcare le classi interne come private o default. Un dettaglio sottile: un metodo public che restituisce un tipo package-private non è molto utile a chi lo chiama da fuori, quindi mantieni coerente la tua superficie pubblica.
Prossimo passo: i membri static
Finora ogni campo e metodo qui appartiene a un'istanza: crei un oggetto con new e poi chiami i metodi su di esso. A volte però un valore o un comportamento appartiene alla classe nel suo insieme ed è condiviso da tutte le istanze, come un contatore di quanti oggetti esistono. È a questo che serve la parola chiave static, ed è il prossimo argomento.
Domande frequenti
Quali sono i quattro modificatori di accesso in Java?
public, private, protected e il default (nessuna parola chiave, detto anche package-private). public è visibile ovunque, private solo nella stessa classe, protected nel package più le sottoclassi, e default solo nello stesso package.
Qual è la differenza tra public e private in Java?
public significa che qualsiasi codice, ovunque, può accedere al membro. private significa che solo il codice nella stessa classe può farlo: sottoclassi e altre classi non lo vedono nemmeno. Di solito rendi i campi private e li esponi tramite metodi public.
Cosa significa quando un campo Java non ha modificatore di accesso?
Nessuna parola chiave significa accesso default (package-private): il membro è visibile a tutte le classi dello stesso package, ma invisibile al di fuori. È più restrittivo di public ma più permissivo di private, ed è facile ometterlo per sbaglio, quindi usalo in modo consapevole.