Saltar al contenido principal

Organizar el código: packages e imports

Cuando cada clase vive en su archivo, una carpeta plana deja de explicar el sistema. Los packages agrupan tipos relacionados y evitan conflictos de nombres.

La estructura y el nombre deben coincidir​

Crearemos este proyecto:

src/
├── app/
│ └── Main.java
├── modelo/
│ └── Alumno.java
└── servicio/
└── Informe.java

La primera línea de cada archivo declara su package. La ruta refleja ese nombre.

src/modelo/Alumno.java:

package modelo;

public class Alumno {
private String nombre;

public Alumno(String nombre) {
this.nombre = nombre;
}

public String getNombre() {
return nombre;
}
}

Una clase usada desde otro package debe ser accesible; por eso Alumno y su constructor son public.

Importar un tipo​

src/servicio/Informe.java:

package servicio;

import modelo.Alumno;

public class Informe {
public String generar(Alumno alumno) {
return "Informe de " + alumno.getNombre();
}
}

import modelo.Alumno permite usar el nombre corto Alumno. El import no copia código ni crea objetos: solo indica qué tipo significa ese nombre.

Punto de entrada​

src/app/Main.java:

package app;

import modelo.Alumno;
import servicio.Informe;

public class Main {
public static void main(String[] args) {
Alumno alumno = new Alumno("Nuria");
Informe informe = new Informe();
System.out.println(informe.generar(alumno));
}
}

Las clases de java.lang, como String y System, se importan automáticamente. Para otros packages de la biblioteca estándar normalmente escribiremos el import explícito.

Compilar respetando los packages​

Desde la raíz del proyecto:

mkdir out
javac -d out src/modelo/Alumno.java src/servicio/Informe.java src/app/Main.java
java -cp out app.Main

-d out pide a javac que genere los .class bajo la estructura adecuada. -cp out indica dónde buscarlos. El nombre ejecutado es app.Main, no una ruta ni Main.java.

Salida:

Informe de Nuria

Nombre completamente cualificado​

También podemos escribir el nombre completo sin import:

modelo.Alumno alumno = new modelo.Alumno("Nuria");

Resulta útil para resolver dos tipos con el mismo nombre, pero abusar de ello hace el código ruidoso. Los imports explícitos suelen ser más legibles.

Visibilidad de package​

Un miembro o clase sin modificador es package-private: solo está disponible dentro del mismo package.

class FormateadorInterno {
String limpiar(String texto) {
return texto.trim();
}
}

Puede servir como detalle interno de servicio, pero app.Main no podría usarlo. No es lo mismo que private, que limita el acceso a la propia clase.

Errores habituales​

  • Declarar package modelo; en un archivo colocado en otra ruta.
  • Ejecutar java Main cuando la clase pertenece a app.
  • Confundir importar con instalar una dependencia.
  • hacer pública cada clase aunque solo sea un detalle interno.
  • Usar import modelo.* para ocultar qué tipos necesita el archivo.

Practica: catálogo organizado​

Enunciado y criterios​

Organiza Producto, Etiqueta, EtiquetaSimple y Main: modelo contiene Producto, servicio contiene el contrato y su implementación, y app crea e imprime un producto. Usa imports explícitos, una ruta coherente y un comando reproducible.

Solución​

src/modelo/Producto.java
src/servicio/Etiqueta.java
src/servicio/EtiquetaSimple.java
src/app/Main.java

Producto.java:

package modelo;
public class Producto {
private String nombre;
public Producto(String nombre) { this.nombre = nombre; }
public String getNombre() { return nombre; }
}

Etiqueta.java:

package servicio;
import modelo.Producto;
public interface Etiqueta {
String crear(Producto producto);
}

Main.java:

package app;
import modelo.Producto;
import servicio.Etiqueta;
import servicio.EtiquetaSimple;
public class Main {
public static void main(String[] args) {
Producto producto = new Producto("Teclado");
Etiqueta etiqueta = new EtiquetaSimple();
System.out.println(etiqueta.crear(producto));
}
}

EtiquetaSimple.java:

package servicio;
import modelo.Producto;
public class EtiquetaSimple implements Etiqueta {
@Override public String crear(Producto producto) { return producto.getNombre(); }
}

Desde la raíz del proyecto, compila los cuatro archivos y ejecuta app.Main:

mkdir out
javac -d out \
src/modelo/Producto.java \
src/servicio/Etiqueta.java \
src/servicio/EtiquetaSimple.java \
src/app/Main.java
java -cp out app.Main

La salida será Teclado. Organizar packages no exige anticipar herramientas ni sintaxis de cursos posteriores.

La organización ya permite que el proyecto final crezca sin mezclar modelo, servicios y entrada de la aplicación.