CodeGym /Cursos /JAVA 25 SELF /Introducción a los módulos: para qué sirven

Introducción a los módulos: para qué sirven

JAVA 25 SELF
Nivel 60 , Lección 0
Disponible

1. Problemas del enfoque antiguo

«Classpath party»: cuando todos están en la misma cocina

Antes de la aparición de los módulos en Java (en la versión 9), todo el código de la aplicación, las bibliotecas y las dependencias se amontonaban en un único gran montón — el classpath. De facto no había fronteras entre bibliotecas: cualquier clase que entrara en el classpath podía ser encontrada y utilizada por cualquiera.

Surgían conflictos de nombres: dos bibliotecas contienen una clase con el mismo nombre completo, por ejemplo com.example.Util — se elegía «alguna» y te enterabas del problema por el comportamiento extraño en tiempo de ejecución.

Un clásico del género — el infierno de dependencias (dependency hell): una biblioteca requiere un logger versión 1.2, otra — 1.3, y en el classpath acaban ambas. La JVM no sabe cuál cargar y te topas con un enigmático NoSuchMethodError.

Además: aunque una clase esté declarada como public pero destinada a uso interno de la biblioteca, otro código puede usarla igualmente — y romper algo en el proceso. Los proyectos grandes se convertían en un campo minado y el mantenimiento — en una aventura.

2. ¿Qué es un módulo en Java?

Módulo: como una caja con agujeros

En Java 9 apareció el sistema de módulos (JPMS). Un módulo es una caja con clases y paquetes en la que tú abres «agujeros»: indicas explícitamente lo que expones hacia fuera (exports) y lo que escondes dentro. Y sí: incluso una clase public no es visible para otros módulos si su paquete no está exportado.

Definición formal

Un módulo es una unidad lógica de partición del código que agrupa paquetes y clases relacionados. Cada módulo declara explícitamente:

  • qué exporta — lo que pone a disposición de otros módulos (exports);
  • y qué importa — de qué otros módulos depende (requires).

Propiedades clave del módulo:

  • Fronteras explícitas. Está claramente definido qué es accesible desde fuera y qué no.
  • Dependencias explícitas. Un módulo no «ve» otro módulo sin un requires explícito.
  • Aislamiento. Los detalles internos pueden ocultarse por completo del mundo exterior.

3. Ventajas de la modularidad

Mejor encapsulación

Apareció un nuevo nivel de visibilidad — a nivel de módulo. Incluso si una clase es public, pero su paquete no se exporta, solo es visible dentro del módulo. Las tripas de la biblioteca ya no «se cuelan» hacia fuera.

Descripción explícita de dependencias

Las dependencias necesarias se enumeran en module-info.java. El compilador y la IDE avisarán con antelación si olvidaste indicar un módulo del que dependes.

Mayor seguridad y fiabilidad

Restringir el acceso a las API internas dificulta la interferencia accidental o intencionada. Esto es crítico para bibliotecas grandes y módulos de plataforma.

Posibilidad de crear una JRE «recortada»

Con jlink puedes construir un runtime mínimo solo con los módulos necesarios. En la nube y en escenarios integrados (embedded) esto ahorra espacio en disco y memoria.

Bonus: arranque más rápido y menor tamaño

No se cargan todos los módulos, sino solo los realmente utilizados — las aplicaciones arrancan más rápido y consumen menos recursos.

4. Dónde se utilizan los módulos

En la biblioteca estándar de Java

Desde Java 9, la propia plataforma es modular. Ejemplos:

  • java.base — módulo base (obligatorio para cualquier programa).
  • java.sql — trabajo con bases de datos.
  • java.xml — trabajo con XML.

Si no utilizas XML, el módulo java.xml ni siquiera formará parte de tu runtime.

En aplicaciones y bibliotecas grandes

Imprescindible para sistemas corporativos, donde decenas de equipos desarrollan partes de un monorepo. La modularidad reduce conflictos y facilita el mantenimiento.

En tus propios proyectos

Incluso en un proyecto personal, los módulos ayudan a entrenar el pensamiento arquitectónico y a mantener el código en orden, evitando el «espagueti».

5. Resumen breve de la sintaxis: module-info.java

Lo más importante: el archivo module-info.java

El archivo module-info.java se encuentra en la raíz de las fuentes del módulo y declara sus límites y dependencias.

module my.awesome.module {
    exports com.example.api;        // Paquete exportado
    requires java.sql;              // Dependencia de un módulo estándar
}

Palabras clave principales:

  • module <nombre> — declara un módulo.
  • exports <paquete> — hace que el paquete sea accesible para otros módulos.
  • requires <módulo> — declara una dependencia de otro módulo.

Ejemplo mínimo:

module com.myproject.core {
    exports com.myproject.core.api;
}

Ejemplo con dependencia:

module com.myproject.app {
    requires com.myproject.core;
    requires java.sql;
}

Capacidades adicionales (breve): para reflexión está opens, para servicios — uses y provides ... with ... (en detalle — en lecciones avanzadas).

6. Detalles útiles

Los módulos son como «pasaportes» para las clases. Antes, cualquier clase con el visado public podía «viajar» por la aplicación. Ahora también hace falta el «pasaporte» modular — la exportación del paquete (exports). Sin él, la clase se queda «en casa».

Dependency hell es un término real. Si has visto ClassNotFoundException: com.google.common.base.Strings, ya has estado allí. Los módulos pretenden ahuyentar a esos «demonios» con fronteras y dependencias estrictas.

Toda Java es ahora modular. Aunque no escribas tus propios módulos, la plataforma ya está dividida en decenas. Prueba el comando:

java --list-modules

Cómo afecta al desarrollo

Gestionas explícitamente la visibilidad del código, y los paquetes internos ya no pasan a estar disponibles para todos «por accidente». La IDE y el compilador detectan los errores antes: si olvidas declarar una dependencia, el proyecto no se construirá. El mantenimiento de sistemas grandes se simplifica: es más fácil entender quién depende de quién y qué se romperá con los cambios.

7. Errores típicos al pasar a módulos

Error n.º 1: olvidaste exportar el paquete, pero la clase es public. Declaraste la clase como public, pero no exportaste su paquete — otros módulos no podrán usarla. El compilador dirá: «El paquete no está exportado por el módulo».

Error n.º 2: no declaraste la dependencia mediante requires. Estás usando un tipo de otro módulo, pero olvidaste añadir requires en module-info.java. Resultado — error de compilación: «el módulo no puede encontrar la clase necesaria».

Error n.º 3: nombres de módulos duplicados. En un proyecto grande, dos módulos recibieron accidentalmente el mismo nombre. La JVM no lo tolera — renómbralos y sigue una convención de nombres uniforme.

Error n.º 4: olvidaste los módulos estándar. Por ejemplo, usas JDBC pero no añadiste requires java.sql;. En Java 8 «se veía de todos modos», pero en 9+ — no.

Error n.º 5: intentar utilizar una clase interna de otro módulo. Si un paquete no se exporta, incluso una clase public sigue siendo invisible desde fuera. Exporta el paquete o mueve el API a una capa «pública».

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION