Interação entre Módulos em Java: Exports e Requires
Aplicações Java complexas são frequentemente divididas em múltiplos módulos para melhorar a organização e a manutenção do código. Para que esses módulos possam colaborar, o Sistema de Módulos da Plataforma Java exige que a comunicação entre eles seja explícita. Este guia explica como fazer um módulo (o provedor) expor suas funcionalidades para que outro (o consumidor) possa utilizá-las.
O projeto de exemplo será composto por dois módulos distintos: operations e demo.
A estrutura de diretórios do projeto será a seguinte:
C:\java
├─── demo
│ ├─── com
│ │ └─── programicio
│ │ └─── hello
│ │ └─── Hello.java
│ └─── module-info.java
└─── operations
├─── com
│ └─── programicio
│ └─── factorial
│ └─── Factorial.java
└─── module-info.javaO Módulo Provedor: operations
Este módulo fornecerá uma funcionalidade de cálculo. Dentro do diretório operations/com/programicio/factorial, o arquivo Factorial.java é definido da seguinte forma:
package com.programicio.factorial;
public class Factorial {
public static int calculate(int n) {
if (n < 0) return -1;
int result = 1;
for (int i = 1; i <= n; i++) {
result = result * i;
}
return result;
}
}Por padrão, o sistema de módulos impõe um encapsulamento forte: todos os pacotes são privados e inacessíveis de fora. Para permitir que outros módulos usem a classe Factorial, seu pacote deve ser exportado explicitamente.
Isso é feito no descritor do módulo, o arquivo operations/module-info.java:
module operations {
exports com.programicio.factorial;
}A diretiva exports torna todos os tipos públicos do pacote com.programicio.factorial visíveis para outros módulos que dependam de operations.
O Módulo Consumidor: demo
Agora, no módulo demo, é preciso declarar que ele irá consumir o módulo operations. Isso é feito no arquivo demo/module-info.java com a diretiva requires.
module demo {
requires operations;
}Com a dependência declarada e o pacote devidamente exportado pelo provedor, a classe Hello pode importar e utilizar a classe Factorial. O arquivo demo/com/programicio/hello/Hello.java terá o seguinte conteúdo:
package com.programicio.hello;
import com.programicio.factorial.Factorial;
public class Hello {
public static void main(String[] args) {
int a = 5;
int b = Factorial.calculate(a);
System.out.printf("Factorial of %d is equal to %d \n", a, b);
}
}Compilação e Execução
O processo deve seguir uma ordem lógica: primeiro compilamos o provedor (operations) e depois o consumidor (demo). Para manter o projeto organizado, os módulos compilados serão salvos em um diretório chamado appmodules.
Abra um terminal, navegue até o diretório c:\java e execute os comandos:
- Compilar o módulo
operations: O comando salva os arquivos.classno diretórioappmodules/operations. - Compilar o módulo
demo: A flag-p(--module-path) informa ao compilador onde encontrar o módulooperationsdo qualdemodepende. - Executar a aplicação: Novamente, a flag
-pé usada para indicar onde encontrar todos os módulos necessários. A flag-m(--module) especifica a classe principal a ser executada.
A sessão completa no terminal ficará assim:
c:\java> javac -d appmodules/operations operations/module-info.java operations/com/programicio/factorial/Factorial.java c:\java> javac -p appmodules -d appmodules/demo demo/module-info.java demo/com/programicio/hello/Hello.java c:\java> java -p appmodules -m demo/com.programicio.hello.Hello Factorial of 5 is equal to 120 c:\java>
Resumo
- Encapsulamento Forte: Por padrão, todos os pacotes de um módulo são privados e inacessíveis a outros módulos.
- Diretiva
exports: Usada no módulo provedor para tornar um pacote público e visível para outros. - Diretiva
requires: Usada no módulo consumidor para declarar que ele depende de outro módulo. - Compilação com
-p: A flag--module-path(ou-p) é essencial para indicar ao compilador onde encontrar os módulos dependentes. - Execução com
-pe-m: Para executar uma aplicação modular, é preciso especificar o caminho dos módulos (-p) e a classe principal a ser executada (-m).