CodeGym /Cursos /JAVA 25 SELF /Erros com modificadores de acesso

Erros com modificadores de acesso

JAVA 25 SELF
Nível 23 , Lição 2
Disponível

1. Introdução

Em Java, os modificadores de acesso são como um sistema de fechaduras em uma casa. Eles determinam quem e de onde pode “entrar” no seu quarto (ou no campo/método da sua classe). Se todas as portas estiverem abertas — qualquer um pode chegar e mudar algo. Se tudo estiver trancado — ninguém vai conseguir quebrar nada, mas você mesmo pode acabar trancado em algum momento.

Lembrando: em Java há quatro níveis principais de acesso:

Modificador Disponível dentro da classe Disponível no pacote Disponível em subclasses Disponível em outros pacotes
private
(package)
protected
(via herança)
public

(package) — é quando o modificador não é especificado explicitamente. Esse membro da classe é visível apenas dentro de um único pacote.

2. Erros comuns com modificadores de acesso

Erro 1: Campos e métodos com modificador padrão (package-private)

O erro mais comum dos iniciantes é esquecer de especificar o modificador de acesso. Como resultado, o campo ou método fica acessível em todo o pacote, embora isso não fosse sua intenção. Isso pode levar a que outra classe (no mesmo pacote, mas não relacionada à sua) consiga alterar o estado interno do seu objeto.

// Erro: o campo name não está protegido!
class User {
    String name; // package-private!
}

Como resultado, qualquer classe desse pacote pode escrever:

User user = new User();
user.name = "Vasya"; // sem restrições!

Erro 2: Violação de encapsulamento — campos públicos (public)

O segundo erro mais comum é declarar os campos da classe como public. Isso é conveniente quando você está apenas aprendendo ou escrevendo um exemplo curto, mas em projetos reais quase sempre é ruim. Você perde o controle sobre quem e como altera seus dados.

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

Agora qualquer código pode fazer:

Account acc = new Account();
acc.balance = -1000000; // E de quem é a culpa agora?

Erro 3: Ausência de getters e setters

Às vezes o desenvolvedor torna os campos private, mas esquece de adicionar métodos para administrá-los. Como resultado, obter ou alterar o valor torna-se impossível mesmo onde seria apropriado.

public class Product {
    private String name;
    // Não há getName() nem setName()
}

Erro 4: Tentar acessar membros private de outra classe

Se você declarou um campo ou método como private, não é possível acessá-lo de outra classe, mesmo que esteja no mesmo pacote. Iniciantes muitas vezes se surpreendem com o motivo de o campo “não ser visível”.

public class User {
    private String password;
}

public class UserService {
    public void resetPassword(User user) {
        // user.password = "123"; // Erro de compilação!
    }
}

Erro 5: Erros com protected

Muitos acham que protected significa “visível em qualquer lugar onde haja herança”. Mas em Java o acesso a membros protected fora do pacote é possível apenas por herança e somente para a subclasse. É uma sutileza fácil de esquecer.

package animals;

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

package zoo;
import animals.Animal;

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

public class NotADog {
    public void test() {
        Animal a = new Animal();
        // a.sleep(); // Erro: não é subclasse!
    }
}

3. Como fazer certo: boas práticas

Regra 1: Por padrão, torne os campos private

Este é o principal princípio do encapsulamento. Os campos devem ficar ocultos para todos, exceto para a própria classe. Se você precisa dar acesso, use getters/setters.

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

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

Regra 2: Exponha apenas os métodos necessários

Se um método deve ser acessível externamente — torne-o public. Se for necessário apenas dentro do pacote — deixe package-private. Se o método for destinado apenas a herdeiros — use protected.

Regra 3: Minimize o escopo de visibilidade

Quanto menor a visibilidade, menor a chance de erros acidentais e de “convidados inesperados”. Não torne métodos e campos public se isso não for necessário.

Regra 4: Use getters e setters para controlar o acesso

Isso permite adicionar lógica extra ao ler/gravar o campo, por exemplo, validação.

public class Account {
    private double balance;

    public void setBalance(double balance) {
        if (balance < 0) {
            throw new IllegalArgumentException("O saldo não pode ser negativo!");
        }
        this.balance = balance;
    }

    public double getBalance() {
        return balance;
    }
}

Regra 5: Não exponha a implementação interna

Se você tem um array ou lista como campo, não o retorne diretamente pelo getter — retorne uma cópia ou forneça apenas os métodos necessários.

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

    // Correto:
    public List<String> getMembers() {
        return new ArrayList<>(members); // retornamos uma cópia
    }
}

4. Exemplos na prática

Suponha que temos a classe LibraryUser, que descreve um usuário de biblioteca.

Exemplo de implementação incorreta

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

Nesse estado, qualquer código pode fazer o que quiser com o objeto:

LibraryUser user = new LibraryUser();
user.name = null;
user.borrowedBooks = -10; // Lógica? Que lógica?

Exemplo de implementação correta com encapsulamento

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--;
        }
    }
}

Agora o código externo não pode alterar diretamente a quantidade de livros emprestados nem o nome do usuário. Tudo é controlado apenas pelos métodos da classe.

5. Particularidades e nuances de implementação

Às vezes parece mais fácil tornar um campo public do que escrever um monte de getters e setters. Mas isso é uma armadilha! Um campo aberto é como a porta do apartamento aberta: sim, é conveniente, mas não é muito seguro.

Outra sutileza: nem sempre é preciso criar getters e setters para todos os campos. Se o valor do campo não deve mudar após a criação do objeto, faça apenas o getter e torne o campo final:

public class Passport {
    private final String number;

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

    public String getNumber() {
        return number;
    }
}

Também lembre-se: se a classe é declarada como public, o nome do arquivo deve coincidir com o nome da classe! Isso não é exatamente sobre modificadores de acesso, mas é um erro muito comum entre iniciantes.

6. Erros típicos ao trabalhar com modificadores de acesso

Erro nº 1: esquecer de especificar o modificador de acesso de um campo ou método. Como resultado, o campo ou método fica acessível em todo o pacote, mesmo que você não quisesse. Sempre especifique o modificador explicitamente, mesmo que a IDE não acuse erro.

Erro nº 2: todos os campos declarados como public. Isso mata o encapsulamento, torna seu código vulnerável e imprevisível. O hábito de “para simplificar” em exemplos não deve chegar ao código de produção.

Erro nº 3: tentar acessar um campo private de outra classe. O Java não permitirá — o compilador irá protegê-lo — mas se você, por acaso, quiser “contornar” isso por meio de reflexão, reflita sobre por que essa necessidade surgiu.

Erro nº 4: esperar que membros protected estejam acessíveis em qualquer lugar onde haja herança. Na verdade, fora do pacote, só é possível acessá-los a partir da subclasse e somente via this ou através de um objeto da subclasse.

Erro nº 5: retornar a coleção interna via getter. Se você retornar a referência do array ou lista internos, o código externo poderá modificá-los, violando os invariantes da classe.

Erro nº 6: falta de controle ao definir valores via setter. Se o valor recebido não for validado, o objeto pode entrar em um estado inválido (por exemplo, saldo negativo).

Erro nº 7: escopo de visibilidade muito amplo para métodos. Às vezes, métodos são tornados public embora sejam necessários apenas dentro do pacote ou da classe. Isso expõe uma API desnecessária e dificulta a manutenção.

Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION