Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

La visibilità

Contesti di visibilità (scope) di metodi e proprietà.
Contesti di visibilità (scope) di metodi e proprietà.
Link copiato negli appunti

Nella programmazione ad oggetti, riveste una grande importanza la definizione dei livelli di visibilità che vengono assegnati ai metodi e alle proprietà di una classe. Infatti, è proprio attraverso la corretta definizione della visibilità applicata ad ogni singolo oggetto che si realizzano gli importanti e vantaggiosi risultati elencati nei paragrafi precedenti, in cui si sono illustrati i capisaldi di OOP.

Ad esempio, è facile comprendere che se un oggetto mettesse a completa disposizione del mondo esterno tutti i suoi metodi e tutte le sue proprietà si perderebbe nel nulla il concetto di incapsulamento. È un po' come se, nel mondo reale, un masterizzatore venisse venduto dando all'utente il completo accesso ai suoi componenti elettronici!

Per assegnare un determinato livello di visibilità ad una proprietà o ad un metodo è necessario utilizzare quelli che in OOP vengono definiti "access specifiers" (specificatori di accesso o anche descrittori di visibilità).

Esistono, in generale, quattro descrittori di visibilità. Alcuni linguaggi, tuttavia, ne utilizzano tre, non considerando il package:

  • public
  • protected
  • private
  • package

In generale, poi, possiamo considerare utile valutare i livelli di visibilità di metodi e proprietà nei seguenti casi:

  • Classe
  • SottoClasse
  • Mondo Esterno
  • Package

Il concetto di Package potrebbe risultare nuovo a chi non ha mai utilizzato un linguaggio di programmazione ad Oggetti. Esso rappresenta un insieme di classi e interfacce che operano nello stesso contesto e che sono raggruppate per consentire un utilizzo più organizzato ed efficiente delle stesse.

In altre parole, un package può essere tranquillamente visto come una libreria di classi che possono essere utilizzate ogniqualvolta se ne presenti la necessità. È, altresì, possibile annidare dei package all'interno di altri package in modo da ottimizzare la suddivisione delle classi (e interfacce).

Il linguaggio Java ha introdotto il concetto di package ed alcuni classici esempi sono i seguenti: java.lang; system.out; javax.swing. Altri linguaggi più recenti, come il C# o i l VB.Net utilizzano la denominazione namespace per definire un concetto del tutto analogo.

Mettendo, ora, in una tabella i descrittori di visibilità da una parte e i contesti in cui essi operano dall'altra, è possibile definire con precisione tutte le casistiche di visibilità su una classe:

Descrittore Classe Package Sottoclasse Mondo Esterno

private
Si No No No

package
Si Si No No

protected
Si Si Si No

public
Si Si Si Si

Vediamo come interpretare la precedente tabella. Nella colonna Classe,ad esempio, è indicata la visibilità che una classe ha dei suoi metodi e delle sue proprietà definiti utilizzando i descrittori presenti nella colonna iniziale.

Come si vede, in tale circostanza, una classe ha sempre accesso e visibilità a tutti i suoi metodi e proprietà a prescindere da quali siano gli access specifiers utilizzati.

Nel caso del Package la situazione è simile: ovvero, tutte le altre classi appartenenti allo stesso Package di una particolare classe hanno sempre accesso ai metodi e proprietà di quest'ultima tranne nel caso in cui siano dichiarati private.

Nella colonna SottoClasse, invece, viene evidenziato come una classe B figlia di una classe A abbia accesso soltanto ai metodi e proprietà di A che sono definiti public o protected.

Infine, l'ultima colonna (Mondo Esterno) evidenzia come soltanto gli attributi e le proprietà di una classe che siano definite public possano essere visibili dall'esterno. In particolare, con la nomenclatura Mondo Esterno si suole identificare ogni classe che non rientri nelle precedenti casistiche esaminate (non sia una sottoclasse della classe in questione e non appartenga allo stesso package).

La scelta degli access specifiers, quando si definisce una classe è tutt'altro che trascurabile. Da essa dipende fortemente la struttura ad oggetti del progetto che si vuole creare e, conseguentemente, l'efficacia dell'implementazione del codice.

Se, ad esempio, si definissero tutti i metodi e le proprietà di un oggetto come public, si perderebbe immediatamente il concetto di incapsulamento in quanto ogni elemento (proprietà o metodo) sarebbe sempre visibile dal mondo esterno. È un po', riprendendo sempre l'esempio del masterizzatore, come se tutti i dettagli interni hardware e software che costituiscono tale dispositivo fossero sempre noti e visibili.

Viceversa, una classe che abbia tutti i metodi (compresi i costruttori) e le proprietà definite come private sarebbe una classe inutilizzabile (esiste tuttavia un tipo particolare di classe, denominata singleton, che vedremo a breve, il cui costruttore è private) poiché nessuno potrebbe avvalersi delle sue caratteristiche.

Vediamo qualche esempio in Java che faciliti la comprensione di quanto esposto:

package Prova;
public class A
{
//Proprietà della classe
private int private_int = 1;
//nessun acces specifier = package access
int package_int = 2;
protected int protected_int = 3;
public int public_int = 4;

//Metodi
private void privateMethod()
{
System.out.println("output con accesso Private");
}

void packageMethod()
{
System.out.println("output con accesso Package");
}

protected void protectedMethod()
{
System.out.println("output con accesso Protected");
}

public void publicMethod()
{
System.out.println("output con accesso Public");
}

public static void main(String[] args)
{
A a = new A (); // Crea un oggetto di classe A
a.privateMethod();
a.packageMethod();
a.protectedMethod();
a.publicMethod();

System.out.println("private_int: " + a.private_int);
System.out.println("package_int: " + a.package_int);
System.out.println("protected_int: " + a.protected_int);
System.out.println("public_int: " + a.public_int);
}
}

L'output dell'esempio precedente sarà il seguente:

output con accesso Private
output con accesso Package
output con accesso Protected
output con accesso Public

private_int: 1
package_int: 2
protected_int: 3
public_int: 4

Tale output mette in evidenza come una classe abbia sempre accesso a tutte le sue proprietà e metodi, a prescindere dall'access specifier definito per essi.

Il prossimo esempio, invece, illustra la situazione indicata dalla seconda colonna della tabella precedente, ovvero l'accesso di una classe ai metodi e alle proprietà di un'altra classe appartenente allo stesso package:

package Prova;

public class B
{
public static void main(String[] args)
{
A a = new A();
    a.privateMethod(); //Errore!!!
a.packageMethod();
a.protectedMethod();
a.publicMethod();


//Errore
System.out.println("private_int: " + a.private_int);

System.out.println("package_int: " + a.package_int);
System.out.println("protected_int: " + a.protected_int);
System.out.println("public_int: " + a.public_int);
}
}

Come si può notare, ci sono un paio di righe di codice evidenziate in rosso in cui viene messo in luce un errore. Infatti, se si provasse a compilare un programma del genere il compilatore identificherebbe un paio incongruenze causate proprio dalle due linee in rosso.

In queste due linee si sta provando ad accedere ad un metodo e ad una proprietà della classe A (definita nell'esempio iniziale) entrambi identificati da un access specifier di tipo private. Questo è l'unico caso in cui si genera un errore poichè tutti gli altri accessi (package, protected e public) non danno alcun problema.

Passiamo ad analizzare il comportamento descritto dalla terza colonna della tabella: l'accesso di una sottoclasse alle proprietà e ai metodi della sua classe padre (nel nostro esempio la sottoclasse appartiene ad un package diverso da quello della clase A per non ricadere nella casistica dell'esempio precedente):

package ProvaB;
import Prova.*;

public class C extends A
{
public static void main(String[] args)
{
A a = new A();
    a.privateMethod(); // Errore
    a.packageMethod(); // Errore
    a.protectedMethod(); // Errore
a.publicMethod();

//Errore
    System.out.println("private_int: " + a.private_int);
//Errore
    System.out.println("package_int: " + a.package_int);
//Errore
    System.out.println("protected_int: "+ a.protected_int);

System.out.println("public_int " + a.public_int);

C c = new C();
c.protectedMethod();
System.out.println("protected_int: " + c. protected_int);
}
}

Come si vede, ora gli errori di compilazione sono parecchi (sempre quelli evidenziati in rosso). Vale la pena di soffermarsi sul terzo e sul sesto errore, ovvero quello sulla invocazione del metodo protectedMethod()e sul tentativo di accesso alla variabile protected_int.

Infatti, dalle informazioni presenti sulla tabella iniziale ci si aspetterebbe che ad una classe figlia sia consentito far riferimento alle proprietà e ai metodi della classe padre quando su di essi è utilizzato l'access specifier protected.

In effetti, ciò è vero nel senso che è consentito ad una istanza di una classe figlia far riferimento ai metodi e alle proprietà implementate nella classe padre ma non è permesso accedere ad essi attraverso una istanza della classe padre. Le ultime tre righe di codice evidenziano proprio tale concetto.

L'ultimo esempio è quello correlato all'accesso del "Mondo Esterno" ai membri di una classe. Viene utilizzata, in tal caso, un'altra classe non derivante da A e appartenente ad un altro package:

package ProvaC;
import Prova.*;
public class D
{
public static void main(String[] args)
{
A a = new A();
a.privateMethod(); //Errore
a.packageMethod(); //Errore
a.protectedMethod(); //Errore

a.publicMethod();

//Errore
System.out.println("private_int: " + a.private_int);
//Errore
System.out.println("package_int: " + a.package_int);
//Errore
System.out.println("protected_int: "+ a.protected_int);

System.out.println("public_int: " + a.public_int);
}
}

Anche qui, le uniche righe di codice immuni da errori sono soltanto quelle in cui viene fatto accesso al metodo e alla proprietà con specificatore di accesso public. Tutte le altre righe sono errate e non verranno accettate dal compilatore.

Ti consigliamo anche