Módulos en Java (Parte II)

Avanzado

Como se mencionó anteriormente, a partir de JDK 9 los paquetes de API de Java se han incorporado en módulos. De hecho, la modularización de la API es uno de los principales beneficios obtenidos con la adición de los módulos. Debido a su función especial, los módulos de API se denominan módulos de plataforma (Platform Modules), y todos sus nombres comienzan con el prefijo java. Estos son algunos ejemplos: java.base, java.desktop y java.xml.

Lectura Recomendada
Módulos en Java (Parte I)

Al modularizar la API, es posible implementar una aplicación sólo con los paquetes que requiere, en lugar de todo el Java Runtime Environment (JRE). Debido al tamaño del JRE completo, esta es una mejora muy importante.

1. java.base y módulos de plataforma

El hecho de que todos los paquetes de la biblioteca Java API estén ahora en módulos da lugar a la siguiente pregunta: ¿Cómo puede el método main() en MiAppModDemo en el ejemplo anterior usar System.out.println() sin especificar una sentencia requires para el módulo que contiene la clase System? Obviamente, el programa no se compilará y ejecutará a menos que System esté presente. La misma pregunta también se aplica al uso de la clase Math en FuncsMateSimples. La respuesta a esta pregunta se encuentra en java.base.

De los módulos de plataforma, el más importante es java.base. Incluye y exporta aquellos paquetes fundamentales para Java, como java.lang, java.io y java.util, entre muchos otros. Debido a su importancia, java.base es automáticamente accesible para todos los módulos. Además, todos los demás módulos requieren automáticamente java.base.

No es necesario incluir una declaración java.base en una declaración de module. (Como punto de interés, no está mal especificar explícitamente java.base; simplemente no es necesario).
Por lo tanto, de la misma manera que java.lang está automáticamente disponible para todos los programas sin el uso de una declaración import, el módulo java.base es automáticamente accesible para todos los programas basados en módulos sin solicitarlo explícitamente. 

Debido a que java.base contiene el paquete java.lang, y java.lang contiene la clase System, MiAppModDemo en el ejemplo anterior puede usar System.out.println() automáticamente sin una declaración requires explícita. Lo mismo se aplica al uso de la clase de Math en FuncsMateSimples, porque la clase de Math también está en java.lang. Como verá cuando comience a crear sus propias aplicaciones basadas en módulos, muchas de las clases de API que comúnmente necesitará están en los paquetes incluidos en java.base. Por lo tanto, la inclusión automática de java.base simplifica la creación de código basado en módulos porque los paquetes centrales de Java son accesibles automáticamente.

2. Exportar a un módulo específico

La forma básica de la declaración exports hace que un paquete sea accesible para todos los demás módulos. Esto es a menudo exactamente lo que quieres. Sin embargo, en algunas situaciones de desarrollo especializado, puede ser deseable hacer que un paquete sea accesible solo para un conjunto específico de módulos, no para todos los demás módulos.

Por ejemplo, un desarrollador de biblioteca (Library Developer) puede querer exportar un paquete de soporte a ciertos otros módulos dentro de la biblioteca, pero no hacer que esté disponible para uso general. Agregar una cláusula to a la declaración exports proporciona un medio por el cual esto se puede lograr. En una declaración exports, la cláusula to especifica una lista de uno o más módulos que tienen acceso al paquete exportado. Además, solo aquellos módulos nombrados en la cláusula to tendrán acceso.

La forma de exports que incluye to se muestra aquí:

exports nombrePaquete to nombreModulos;

Aquí, nombreModulos es una lista de módulos separados por comas a los que el módulo de exportación otorga acceso.

Puede probar la cláusula to cambiando el archivo module-info.java para el módulo appfuncs, como se muestra aquí:

module appfuncs{
    // Exporta el paquete appfuncs.funcsimples a appinicio
    exports appfuncs.funcsimples to appinicio;
}

Ahora, funcsimples se exporta solo a appinicio y a ningún otro módulo. Después de hacer este cambio, puede recompilar la aplicación usando este comando javac:

javac -d appmodules --module-source-path appsrc 
appinicio appsrc\appinicio\appinicio\midemoappmod\MiAppModDemo.java

Después de compilar, puede ejecutar la aplicación como se muestra anteriormente.

Este ejemplo también usa otra nueva característica relacionada con el módulo proporcionada por JDK 9. Mire de cerca el comando javac anterior. Primero, observe que especifica la opción –module-source-path, esta compila automáticamente los archivos en el árbol bajo el directorio especificado, que es appsrc en este ejemplo. La opción –module-source-path se debe utilizar con la opción -d para garantizar que los módulos compilados se almacenen en sus directorios adecuados bajo appmodules.

3. Uso de requires transitive

Considere una situación en la que hay tres módulos, A, B y C, que tienen las siguientes dependencias:

A requiere B.
B requiere C.

Dada esta situación, está claro que, dado que A depende de B y B depende de C, A tiene una dependencia indirecta de C. Siempre que A no utilice directamente ninguno de los contenidos de C, entonces simplemente puede hacer que A requiera B en su archivo de información de módulo, y haga que B exporte los paquetes requeridos por A en su archivo de información de módulo, como se muestra aquí:

//Archivo module-info de A:
module A{
   requires B;
}
//Archivo module-info de B:
module B{
   exports algun.paquete;
   requires C;
}

Aquí, algun.paquete es un marcador de posición para el paquete exportado por B y utilizado por A. Aunque esto funciona siempre que A no necesite usar nada definido en C, se produce un problema si A desea acceder a un tipo en C. En este caso, hay dos soluciones.

  • La primera solución es simplemente agregar una declaración requires C al archivo de A, como se muestra aquí:
//El archivo module-info de A se actualizó para requerir explícitamente C:
module A{
   requires B;
   requires C;
}

Esta solución ciertamente funciona, pero si B será utilizada por muchos módulos, debe agregar C a todas las definiciones de módulos que requieren B. Esto no solo es tedioso; también es propenso a errores. Afortunadamente, hay una mejor solución. Puede crear una dependencia implícita de C. La dependencia implícita también se conoce como legibilidad implícita.

Para crear una dependencia implícita, agregue la palabra clave transitive después de requires en la cláusula que requiere el módulo sobre el que se necesita una legibilidad implícita. En el caso de este ejemplo, cambiarías el archivo module-info de B como se muestra aquí:

//Archivo module-info de B:
module B{
   exports algun.paquete;
   requires transitive C;
}

Aquí, C ahora se requiere como transitivo. Después de hacer este cambio, cualquier módulo que dependa de B también dependerá automáticamente de C. Por lo tanto, A tendría automáticamente acceso a C.

Módulos en Java
  • java.base
  • Uso de requires transitive
Sending
User Review
5 (2 votes)

Sobre el Autor:

Hey hola! Yo soy Alex Walton y tengo el placer de compartir conocimientos hacía ti sobre el tema de Programación en Java, desde cero, Online y Gratis.

Deja una Respuesta

*

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.