CodeGym /Cursos /JAVA 25 SELF /Encapsulamento por meio de módulos: exportação e importaç...

Encapsulamento por meio de módulos: exportação e importação

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

1. Exportação de pacotes: quem vê seu código?

No mundo pré-Java 9 tudo era simples — só que não muito seguro. Se uma classe fosse public, qualquer um poderia vê-la tendo acesso ao JAR ou ao classpath. Isso levava ao uso acidental ou intencional de classes internas e até pacotes inteiros fora do seu projeto. Sim, dava para esconder algo com private ou package-private, mas se a classe é public, ela fica acessível a todos.

Com a chegada dos módulos isso mudou. Agora, mesmo que a classe seja declarada como public, não é possível usá-la de outro módulo se o pacote em que ela está não for exportado explicitamente via module-info.java.

Como isso funciona na prática

Suponha que você tenha a seguinte estrutura de projeto:

my-app/
  src/
    core/
      com/example/core/
        CoreService.java
        InternalHelper.java
      module-info.java
    app/
      com/example/app/
        Main.java
      module-info.java

No arquivo core/module-info.java você escreve:

module core {
    exports com.example.core;
}

Então apenas as classes do pacote com.example.core estarão disponíveis para outros módulos. Tudo o que estiver em outros pacotes (por exemplo, com.example.core.internal) ficará invisível — mesmo que haja classes public lá. Isso é o encapsulamento modular.

Mini demo: “visível/invisível”

Dentro de core:

// com/example/core/CoreService.java
package com.example.core;

public class CoreService {
    public void doWork() {
        System.out.println("Work done!");
    }
}

Dentro de app:

// com/example/app/Main.java
package com.example.app;

import com.example.core.CoreService;

public class Main {
    public static void main(String[] args) {
        CoreService service = new CoreService();
        service.doWork();
    }
}

Se você remover a linha exports com.example.core; de core/module-info.java, na compilação você receberá o erro:

error: package com.example.core is not visible

Mesmo que CoreService seja public!

2. Importação de dependências: requires e isolamento rigoroso

No JPMS não dá para simplesmente usar uma classe de outro módulo. É preciso declarar explicitamente a dependência com requires.

Exemplo

No arquivo app/module-info.java:

module app {
    requires core;
}

Agora o módulo app pode usar tudo o que o módulo core exporta.

Se você esquecer de escrever requires core;, receberá um erro de compilação:

error: package com.example.core is not visible

ou

error: cannot access CoreService

Ponto importante

  • requires atua no nível de módulos, não de pacotes.
  • Você não pode “importar” apenas um pacote — apenas todo o módulo exportado.

3. Comparação: módulos vs. public/private

Os módulos adicionam um novo nível de encapsulamento, que fica “acima” de classes e pacotes.

Nível O que regula? Como funciona?
private Acesso dentro da classe Apenas dentro do próprio arquivo
package-private (padrão) Acesso dentro do pacote Todas as classes no mesmo pacote
public Acesso para todos Qualquer código em qualquer lugar
module Acesso entre módulos Somente pacotes exportados

Ideia principal:

  • Uma classe pode ser public, mas, se o seu pacote não for exportado, ela fica acessível apenas dentro do módulo.
  • Exportar um pacote com exports é como uma “janela” pela qual seu código é visível para outros módulos.

Exemplo: implementação ocultada

// com/example/core/internal/SecretSauce.java
package com.example.core.internal;

public class SecretSauce {
    public void addMagic() {}
}

Se você NÃO exportar o pacote com.example.core.internal no module-info.java, nenhum módulo externo conseguirá usar essa classe, mesmo que ela seja public!

4. Exemplo: core e app — API e implementação

Vamos ver um cenário típico: o módulo core fornece o API, o módulo app — o utiliza.

Estrutura:

core/
  com/example/core/
    CoreAPI.java
  com/example/core/impl/
    CoreImpl.java
  module-info.java

app/
  com/example/app/
    Main.java
  module-info.java

core/module-info.java:

module core {
    exports com.example.core; // Apenas o API!
    // Não exportamos com.example.core.impl
}

app/module-info.java:

module app {
    requires core;
}

com/example/core/CoreAPI.java:

package com.example.core;

public interface CoreAPI {
    void doSomething();
}

com/example/core/impl/CoreImpl.java:

package com.example.core.impl;

import com.example.core.CoreAPI;

public class CoreImpl implements CoreAPI {
    @Override
    public void doSomething() {
        System.out.println("Doing something!");
    }
}

com/example/app/Main.java:

package com.example.app;

import com.example.core.CoreAPI;
// import com.example.core.impl.CoreImpl; // Isso causará um erro de compilação!

public class Main {
    public static void main(String[] args) {
        // CoreImpl impl = new CoreImpl(); // Erro! O pacote não é exportado.
        // Você só pode usar o que é visível pelo API.
    }
}

Resultado:

  • O módulo app vê apenas o que core exporta.
  • A implementação (impl) fica oculta, mesmo que as classes lá sejam public.

5. Prática: brincando com export e visibilidade

Passo 1: remover o export

Em core/module-info.java comente a linha:

// exports com.example.core;

Agora tente compilar o projeto. Espere um erro de compilação no módulo app — ele não verá as classes de com.example.core.

Passo 2: exportar somente o API

Restaure a linha exports com.example.core;, mas não exporte com.example.core.impl. Tente, no módulo app, importar uma classe de impl. Você novamente receberá um erro de compilação — está tudo correto!

Passo 3: classe public em um pacote não exportado

Crie uma classe public em um pacote não exportado. Tente usá-la de outro módulo — não vai funcionar. Isso é o encapsulamento modular em ação.

6. Como isso aparece na IDE e “em termos simples”

  • Ao tentar importar uma classe de um pacote não exportado, a IDE destacará um erro.
  • A dica explicará que o pacote não é exportado pelo módulo.
  • Adicione exports para o pacote necessário — o erro desaparecerá.

Esquema: níveis de acesso

+---------------------+
|      MODUL’         |
|  (module-info.java) |
+---------------------+
       |
       v
+---------------------+
|     PAKETY          |
| (package, export)   |
+---------------------+
       |
       v
+---------------------+
|      KLASSY         |
| (public/private)    |
+---------------------+
       |
       v
+---------------------+
|   METODY/POLYA      |
| (public/private)    |
+---------------------+

7. Nuances e particularidades importantes

Exportação “apenas para amigos”: exports ... to

Às vezes é necessário exportar um pacote apenas para módulos específicos (por exemplo, para testes ou extensões especiais):

exports com.example.core.internal to my.special.module, my.test.module;

Agora somente os módulos indicados verão esse pacote.

É possível exportar vários pacotes?

Sim! Basta adicionar novas linhas de exports:

exports com.example.core;
exports com.example.core.api;

É possível exportar “tudo”?

Não. No sistema de módulos do Java você sempre especifica explicitamente o que exportar. Essa é a sua força.

8. Erros típicos ao trabalhar com encapsulamento modular

Erro nº 1: Esperar que uma classe public esteja sempre visível.
Se o pacote não for exportado via module-info.java, a classe ficará invisível para outros módulos, mesmo que ela seja public. Esse é o “tropeço” mais comum para iniciantes.

Erro nº 2: Esquecer de declarar requires.
Se o seu módulo usa classes de outro módulo, mas não declarou requires, você terá um erro de compilação. Não se esqueça de descrever explicitamente as dependências.

Erro nº 3: Tentar exportar o mesmo pacote a partir de dois módulos.
No JPMS cada pacote pode ser exportado por apenas um módulo. Se você violar essa regra, o compilador vai barrar.

Erro nº 4: Estrutura incorreta — module-info.java no lugar errado.
O arquivo module-info.java deve estar na raiz do código-fonte do módulo; caso contrário, o módulo não será reconhecido.

Erro nº 5: Dependências cíclicas entre módulos.
Se o módulo A requer B e B requer A — você terá um erro. Evite ciclos no grafo de dependências.

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