Package e Controllo degli Accessi in Java

Concetti Chiave
  • Utilizzando i package in Java, bisogna tenere presente che le regole di visibilità e accesso sono influenzate dalla posizione delle classi nei package.
  • In particolare, le classi in package differenti possono avere accesso limitato ai membri di altre classi a seconda dei modificatori di accesso utilizzati.

Package e Accesso ai Membri

Nelle lezioni precedenti, abbiamo esaminato vari aspetti del meccanismo di controllo degli accessi di Java e dei suoi modificatori di accesso.

Per esempio, sappiamo già che l'accesso a un membro private di una classe è concesso solo agli altri membri della stessa classe.

I package aggiungono un'ulteriore dimensione al controllo degli accessi. Come vedremo, Java fornisce molti livelli di protezione per consentire un controllo fine sulla visibilità di variabili e metodi all'interno di classi, sottoclassi e package.

Le classi e i package sono entrambi strumenti per incapsulare e contenere lo spazio dei nomi e l'ambito di variabili e metodi. I package fungono da contenitori per classi e altri package subordinati. Le classi fungono da contenitori per dati e codice. La classe è la più piccola unità di astrazione di Java. Per quanto riguarda l'interazione tra classi e package, Java prevede quattro categorie di visibilità per i membri di classe:

  • Sottoclassi nello stesso package
  • Non-sottoclassi nello stesso package
  • Sottoclassi in package differenti
  • Classi che non sono né nello stesso package né sottoclassi

I tre modificatori di accesso, private, public e protected, forniscono una varietà di modi per produrre i molti livelli di accesso richiesti da queste categorie. La Tabella che segue riassume le interazioni.

Categoria private Nessun Modificatore protected public
Stessa classe
Sottoclasse stesso package No
Non-sottoclasse stesso package No
Sottoclasse altro package No No
Non-sottoclasse altro package No No No
Tabella 1: Accesso ai Membri di Classe

Anche se il meccanismo di controllo degli accessi di Java può sembrare complicato, possiamo semplificarlo come segue. Qualsiasi cosa dichiarata public può essere acceduta da classi differenti e da package differenti. Qualsiasi cosa dichiarata private non può essere vista al di fuori della sua classe. Quando un membro non ha una specificazione esplicita di accesso, è visibile sia alle sottoclassi sia alle altre classi nello stesso package. Questo è l'accesso predefinito. Se si vuole permettere che un elemento sia visibile al di fuori del proprio package, ma solo a classi che derivano direttamente dalla propria classe, allora bisogna dichiarare quell'elemento protected.

La Tabella di sopra si applica solo ai membri delle classi. Una classe non annidata ha solo due possibili livelli di accesso: predefinito e public. Quando una classe è dichiarata public, è accessibile al di fuori del suo package. Se una classe ha accesso predefinito, allora può essere acceduta solo da altro codice all'interno dello stesso package. Quando una classe è public, deve essere l'unica classe public dichiarata nel file, e il file deve avere lo stesso nome della classe.

Consiglio

Accessibilità influenzata dai moduli

La funzionalità dei moduli può influenzare anche l'accessibilità. Vedremo i moduli nelle prossime lezioni.

Un Esempio di Accesso

Il seguente esempio mostra tutte le combinazioni dei modificatori di controllo degli accessi. Questo esempio ha due package e cinque classi. Le classi dei due package differenti devono essere salvate in directory denominate con il nome dei rispettivi package — in questo caso, p1 e p2.

Il codice sorgente per il primo package definisce tre classi: Protezione, Derivata e StessoPackage. La prima classe definisce quattro variabili int in ciascuna delle modalità di protezione legali. La variabile n è dichiarata con la protezione predefinita, n_pri è private, n_pro è protected e n_pub è public.

Ogni classe successiva in questo esempio proverà ad accedere alle variabili in un'istanza di questa classe. Le linee che non compileranno a causa di restrizioni di accesso sono commentate. Prima di ciascuna di queste linee, c'è un commento che elenca i luoghi da cui questo livello di protezione consentirebbe l'accesso.

La seconda classe, Derivata, è una sottoclasse di Protezione nello stesso package, p1. Questo concede a Derivata l'accesso a ogni variabile in Protezione tranne che a n_pri, quella private.

La terza classe, StessoPackage, non è una sottoclasse di Protezione, ma si trova nello stesso package e ha quindi accesso a tutte tranne che a n_pri.

Questo è il file Protezione.java:

package p1;

public class Protezione {
    int n = 1;
    private int n_pri = 2;
    protected int n_pro = 3;
    public int n_pub = 4;

    public Protezione() {
        System.out.println("costruttore base");
        System.out.println("n = " + n);
        System.out.println("n_pri = " + n_pri);
        System.out.println("n_pro = " + n_pro);
        System.out.println("n_pub = " + n_pub);
    }
}

Questo è il file Derivata.java:

package p1;

class Derivata extends Protezione {
    Derivata() {
        System.out.println("costruttore derivato");
        System.out.println("n = " + n);

        // solo nella classe
        // System.out.println("n_pri = " + n_pri);

        System.out.println("n_pro = " + n_pro);
        System.out.println("n_pub = " + n_pub);
    }
}

Questo è il file StessoPackage.java:

package p1;

class StessoPackage {
    StessoPackage() {
        Protezione p = new Protezione();
        System.out.println("costruttore stesso package");
        System.out.println("n = " + p.n);

        // solo nella classe
        // System.out.println("n_pri = " + p.n_pri);

        System.out.println("n_pro = " + p.n_pro);
        System.out.println("n_pub = " + p.n_pub);
    }
}

Segue il codice sorgente per l'altro package, p2. Le due classi definite in p2 coprono le altre due condizioni che sono influenzate dal controllo degli accessi. La prima classe, Protezione2, è una sottoclasse di p1.Protezione. Questo concede accesso a tutte le variabili di p1.Protezione tranne n_pri (perché è private) e n, la variabile dichiarata con la protezione predefinita. Ricordare che la protezione predefinita consente l'accesso solo all'interno della classe o del package, non a sottoclassi esterne al package. Infine, la classe AltroPackage ha accesso solo a una variabile, n_pub, che è stata dichiarata public.

Questo è il file Protezione2.java:

package p2;

class Protezione2 extends p1.Protezione {
    Protezione2() {
        System.out.println("costruttore derivato altro package");

        // solo classe o package
        // System.out.println("n = " + n);

        // solo classe
        // System.out.println("n_pri = " + n_pri);

        System.out.println("n_pro = " + n_pro);
        System.out.println("n_pub = " + n_pub);
    }
}

Questo è il file AltroPackage.java:

package p2;

class AltroPackage {
    AltroPackage() {
        p1.Protezione p = new p1.Protezione();
        System.out.println("costruttore altro package");

        // solo classe o package
        // System.out.println("n = " + p.n);

        // solo classe
        // System.out.println("n_pri = " + p.n_pri);

        // classe, sottoclasse o package
        // System.out.println("n_pro = " + p.n_pro);

        System.out.println("n_pub = " + p.n_pub);
    }
}

Se si vogliono provare questi due package, qui ci sono due file di test che si possono usare. Quello per il package p1 è mostrato qui:

// Demo package p1.
package p1;

// Istanziare le varie classi in p1.
public class Demo {
    public static void main(String[] args) {
        Protezione ob1 = new Protezione();
        Derivata ob2 = new Derivata();
        StessoPackage ob3 = new StessoPackage();
    }
}

Il file di test per p2 è mostrato di seguito:

// Demo package p2.
package p2;

// Istanziare le varie classi in p2.
public class Demo {
    public static void main(String[] args) {
        Protezione2 ob1 = new Protezione2();
        AltroPackage ob2 = new AltroPackage();
    }
}