CodeGym /Corsi /JAVA 25 SELF /Errori con i modificatori di accesso

Errori con i modificatori di accesso

JAVA 25 SELF
Livello 23 , Lezione 2
Disponibile

1. Introduzione

In Java i modificatori di accesso sono come un sistema di serrature in una casa. Determinano chi e da dove può «entrare» nella tua stanza (o nel campo/metodo della tua classe). Se tutte le porte sono aperte, chiunque può venire e cambiare qualcosa. Se è tutto chiuso, nessuno potrà rompere nulla, ma anche tu a un certo punto potresti ritrovarti chiuso dentro.

Ricordiamo che in Java esistono quattro livelli di accesso principali:

Modificatore Disponibile all'interno della classe Disponibile nel package Disponibile nelle sottoclassi Disponibile in altri package
private
(package)
protected
(tramite ereditarietà)
public

(package) è quando il modificatore non è specificato esplicitamente. Un membro del genere è visibile solo all’interno dello stesso package.

2. Errori tipici con i modificatori di accesso

Errore 1: Campi e metodi con modificatore predefinito (package-private)

L’errore più comune dei principianti è dimenticare di indicare il modificatore di accesso. Di conseguenza il campo o il metodo diventa accessibile in tutto il package, anche se non era nelle tue intenzioni. Questo può portare al fatto che un’altra classe (nello stesso package, ma non correlata alla tua) possa modificare lo stato interno del tuo oggetto.

// Errore: il campo name non è protetto!
class User {
    String name; // package-private!
}

Di conseguenza, qualsiasi classe di questo package può scrivere:

User user = new User();
user.name = "Vasya"; // nessuna restrizione!

Errore 2: Violazione dell’incapsulamento — campi pubblici (public)

Il secondo errore per diffusione è dichiarare i campi della classe come public. È comodo quando stai imparando o scrivi un esempio breve, ma nei progetti reali è quasi sempre una cattiva idea. Perdi il controllo su chi e come modifica i tuoi dati.

public class Account {
    public double balance; // PERICOLOSO!
}

Ora qualsiasi codice può fare:

Account acc = new Account();
acc.balance = -1000000; // E di chi è la colpa adesso?

Errore 3: Mancanza di getter e setter

A volte lo sviluppatore rende i campi private ma dimentica di aggiungere i metodi per gestirli. Di conseguenza non è possibile ottenere o modificare il valore neppure dove sarebbe appropriato.

public class Product {
    private String name;
    // Non ci sono né getName() né setName()
}

Errore 4: Tentativo di accedere a private-membri da un’altra classe

Se hai dichiarato un campo o un metodo come private, non è possibile accedervi da un’altra classe, anche se si trova nello stesso package. I principianti spesso si stupiscono del perché «non si vede» il campo.

public class User {
    private String password;
}

public class UserService {
    public void resetPassword(User user) {
        // user.password = "123"; // Errore di compilazione!
    }
}

Errore 5: Errori con protected

Molti ritengono che protected significhi «visibile ovunque ci sia ereditarietà». Ma in Java l’accesso ai membri protected al di fuori del package è possibile solo tramite ereditarietà e solo per la sottoclasse. È una sottigliezza che è facile lasciarsi sfuggire.

package animals;

public class Animal {
    protected void sleep() {}
}

package zoo;
import animals.Animal;

public class Dog extends Animal {
    public void test() {
        sleep(); // OK — sottoclasse
    }
}

public class NotADog {
    public void test() {
        Animal a = new Animal();
        // a.sleep(); // Errore: non è una sottoclasse!
    }
}

3. Come fare bene: buone pratiche

Regola 1: Di default rendi i campi private

Questo è il principio fondamentale dell’incapsulamento. I campi devono essere nascosti a tutti tranne che alla stessa classe. Se serve dare accesso, usa getter/setter.

public class Book {
    private String title;
    private int pages;

    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
}

Regola 2: Esporre solo i metodi necessari

Se un metodo deve essere accessibile dall’esterno — rendilo public. Se serve solo all’interno del package — lascialo package-private. Se è destinato solo ai sottotipi — usa protected.

Regola 3: Minimizza l’ambito di visibilità

Più piccola è l’area di visibilità, minore è la probabilità di errori accidentali e di «ospiti indesiderati». Non rendere metodi e campi public se non è necessario.

Regola 4: Usa getter e setter per controllare l’accesso

Questo consente di aggiungere logica aggiuntiva durante la lettura/scrittura del campo, ad esempio la validazione.

public class Account {
    private double balance;

    public void setBalance(double balance) {
        if (balance < 0) {
            throw new IllegalArgumentException("Il saldo non può essere negativo!");
        }
        this.balance = balance;
    }

    public double getBalance() {
        return balance;
    }
}

Regola 5: Non esporre l’implementazione interna

Se hai un array o una lista come campo, non restituirla direttamente tramite il getter — restituisci una copia oppure fornisci solo i metodi necessari.

public class Team {
    private List<String> members = new ArrayList<>();

    // Corretto:
    public List<String> getMembers() {
        return new ArrayList<>(members); // restituiamo una copia
    }
}

4. Esempi pratici

Supponiamo di avere una classe LibraryUser che descrive un utente della biblioteca.

Esempio di implementazione errata

public class LibraryUser {
    public String name;
    public int borrowedBooks;
}

In questa forma, qualsiasi codice può fare qualsiasi cosa con l’oggetto:

LibraryUser user = new LibraryUser();
user.name = null;
user.borrowedBooks = -10; // Logica? Quale logica?

Esempio di implementazione corretta con incapsulamento

public class LibraryUser {
    private String name;
    private int borrowedBooks;

    public LibraryUser(String name) {
        this.name = name;
        this.borrowedBooks = 0;
    }

    public String getName() {
        return name;
    }

    public int getBorrowedBooks() {
        return borrowedBooks;
    }

    public void borrowBook() {
        borrowedBooks++;
    }

    public void returnBook() {
        if (borrowedBooks > 0) {
            borrowedBooks--;
        }
    }
}

Ora il codice esterno non può modificare direttamente il numero di libri presi o il nome dell’utente. Tutto è controllato solo attraverso i metodi della classe.

5. Aspetti e particolarità dell’implementazione

A volte sembra più semplice rendere un campo public che scrivere un mucchio di getter e setter. Ma è una trappola! Un campo aperto è come una porta di casa spalancata: sì, è comodo, ma non è molto sicuro.

Un’altra sottigliezza: non sempre è necessario creare getter e setter per tutti i campi. Se il valore del campo non deve cambiare dopo la creazione dell’oggetto, fornisci solo il getter e rendi il campo final:

public class Passport {
    private final String number;

    public Passport(String number) {
        this.number = number;
    }

    public String getNumber() {
        return number;
    }
}

Ricorda anche: se la classe è dichiarata come public, il nome del file deve coincidere con il nome della classe! Non è esattamente sui modificatori di accesso, ma è un errore molto comune tra i principianti.

6. Errori tipici nell’uso dei modificatori di accesso

Errore n. 1: hai dimenticato di specificare il modificatore di accesso per un campo o un metodo. Di conseguenza il campo o il metodo diventa accessibile all’interno di tutto il package, anche se non lo volevi. Indica sempre esplicitamente il modificatore, anche se l’IDE non segnala problemi.

Errore n. 2: tutti i campi sono dichiarati come public. Questo uccide l’incapsulamento, rende il tuo codice vulnerabile e imprevedibile. L’abitudine presa dagli esempi «per semplicità» non dovrebbe finire nel codice di produzione.

Errore n. 3: tentativo di accedere a un campo private da un’altra classe. Java non lo permetterà — il compilatore ti proteggerà, ma se ti è venuta l’idea di «aggirarlo» tramite reflection, chiediti perché sia sorta questa necessità.

Errore n. 4: aspettarsi che i membri protected siano disponibili ovunque ci sia ereditarietà. In realtà, al di fuori del package vi si può accedere solo da una sottoclasse e solo tramite this o tramite un oggetto della sottoclasse.

Errore n. 5: restituire la collezione interna tramite il getter. Se restituisci un riferimento all’array o alla lista interna, il codice esterno potrà modificarli, violando gli invarianti della classe.

Errore n. 6: assenza di controllo quando si imposta un valore tramite il setter. Se non verifichi il valore in ingresso, puoi ottenere uno stato non corretto dell’oggetto (ad esempio, un saldo negativo).

Errore n. 7: ambito di visibilità dei metodi troppo ampio. A volte i metodi vengono resi public anche se servono solo all’interno del package o della classe. Questo espone API superfluo e rende la manutenzione più difficile.

1
Compito
JAVA 25 SELF, livello 23, lezione 2
Bloccato
Archivio semplice della concessionaria 🚗
Archivio semplice della concessionaria 🚗
1
Compito
JAVA 25 SELF, livello 23, lezione 2
Bloccato
Gestione sicura dei prezzi nel negozio online 💰
Gestione sicura dei prezzi nel negozio online 💰
1
Compito
JAVA 25 SELF, livello 23, lezione 2
Bloccato
Protezione delle password: Niente scappatoie! 🔐
Protezione delle password: Niente scappatoie! 🔐
1
Compito
JAVA 25 SELF, livello 23, lezione 2
Bloccato
Chi può "parlare" allo zoo? 🐾
Chi può "parlare" allo zoo? 🐾
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION