4 minutos

Guía de Paquetes en Java

Guía de Paquetes en Java

1. Introducción

En este breve tutorial, cubriremos los conceptos básicos de los paquetes en Java. Veremos cómo crear paquetes y acceder a los tipos que colocamos en ellos.

También discutiremos las convenciones de nomenclatura y cómo se relacionan con la estructura de directorios subyacente.

Finalmente, compilaremos y ejecutaremos nuestras clases Java empaquetadas.

2. Resumen de los Paquetes en Java

En Java, usamos paquetes (package) para agrupar clases relacionadas, interfaces y subpaquetes.

Los principales beneficios de hacer esto son:

  • Facilitar la búsqueda de tipos relacionados: los paquetes generalmente contienen tipos que están lógicamente relacionados.
  • Evitar conflictos de nombres: un paquete nos ayudará a identificar de manera única una clase; por ejemplo, podríamos tener clases com.javamagician.App y com.ejemplo.App.
  • Controlar el acceso: podemos controlar la visibilidad y el acceso a tipos combinando paquetes y modificadores de acceso.

A continuación, veamos cómo podemos crear y usar paquetes en Java.

3. Creación de un Paquete

Para crear un paquete, debemos usar la declaración package agregándola como la primera línea de código en un archivo.

Coloquemos un tipo en un paquete llamado com.javamagician.paquetes:

package com.javamagician.paquetes;

Es muy recomendable colocar cada nuevo tipo en un paquete. Si definimos tipos y no los colocamos en un paquete, irán al paquete predeterminado (default) o sin nombre. El uso de paquetes predeterminados tiene algunas desventajas:

  • Perdemos los beneficios de tener una estructura de paquetes y no podemos tener subpaquetes.
  • No podemos importar los tipos en el paquete predeterminado desde otros paquetes.
  • Los ámbitos de acceso protected y package-private no tendrían sentido.

Según la especificación del lenguaje Java, los paquetes sin nombre son proporcionados por la Plataforma Java SE principalmente por conveniencia al desarrollar aplicaciones pequeñas o temporales o cuando se está comenzando el desarrollo.

Por lo tanto, debemos evitar el uso de paquetes sin nombre o predeterminados en aplicaciones del mundo real.

3.1. Convenciones de Nomenclatura

Para evitar paquetes con el mismo nombre, seguimos algunas convenciones de nomenclatura:

  • Definimos los nombres de nuestros paquetes en minúsculas.
  • Los nombres de los paquetes están separados por puntos.
  • Los nombres también están determinados por la empresa u organización que los crea.

Para determinar el nombre del paquete según una organización, generalmente comenzamos revirtiendo la URL de la empresa. Después de eso, la convención de nomenclatura es definida por la empresa y puede incluir nombres de divisiones y proyectos.

Por ejemplo, para crear un paquete a partir de javamagician.com, primero lo invertimos:

com.javamagician

Luego podemos definir subpaquetes de esto, como com.javamagician.paquetes o com.javamagician.paquetes.dominio.

3.2. Estructura de Directorios

Los paquetes en Java corresponden a una estructura de directorios.

Cada paquete y subpaquete tiene su propio directorio. Entonces, para el paquete com.javamagician.paquetes, deberíamos tener una estructura de directorios com -> javamagician -> paquetes`.

com
└ javamagician
  └ paquetes

La mayoría de las IDE ayudarán a crear esta estructura de directorios basada en los nombres de nuestros paquetes, por lo que no es necesario crearlos manualmente.

4. Uso de Miembros del Paquete

Comencemos por definir una clase ElementoPorHacer en un subpaquete llamado dominio:

package com.javamagician.paquetes.dominio;

public class ElementoPorHacer {
    private Long id;
    private String description;

    // getters y setters estándar
}

4.1. Importaciones

Para usar nuestra clase ElementoPorHacer desde una clase en otro paquete, debemos importarla. Una vez importada, podemos acceder a ella por su nombre.

Podemos importar un solo tipo de un paquete o usar un asterisco para importar todos los tipos en un paquete.

Importemos todo el subpaquete dominio:

import com.javamagician.paquetes.dominio.*;

Ahora, importemos solo la clase ElementoPorHacer:

import com.javamagician.paquetes.dominio.ElementoPorHacer;

El JDK y otras bibliotecas de Java también vienen con sus propios paquetes. Podemos importar clases preexistentes que queremos usar en nuestro proyecto de la misma manera.

Por ejemplo, importemos la interfaz List del núcleo de Java y la clase ArrayList:

import java.util.ArrayList;
import java.util.List;

Luego podemos usar estos tipos en nuestra aplicación simplemente usando su nombre:

public class ListaPorHacer {
    private List<ElementoPorHacer> elementosPorHacer;

    public void agregarElementoPorHacer(ElementoPorHacer elementoPorHacer) {
        if (elementosPorHacer == null) {
            elementosPorHacer = new ArrayList<ElementoPorHacer>();
        }
        elementosPorHacer.add(elementoPorHacer);
    }
}

Aquí hemos utilizado nuestras nuevas clases junto con las clases del núcleo de Java para crear una lista de elementos "Por Hacer".

4.2. Nombre Completamente Calificado

A veces, podemos estar utilizando dos clases con el mismo nombre de diferentes paquetes. Por ejemplo, podríamos estar usando java.sql.Date y java.util.Date. Cuando nos encontramos con conflictos de nombres, necesitamos usar un nombre de clase completamente calificado para al menos una de las clases.

Usemos ElementoPorHacer con un nombre completamente calificado:

public class ListaPorHacer {
    private List<com.javamagician.paquetes.dominio.ElementoPorHacer> elementosPorHacer;

    public void agregarElementoPorHacer(com.javamagician.paquetes.dominio.ElementoPorHacer elementoPorHacer) {
        if (elementosPorHacer == null) {
            elementosPorHacer = new ArrayList<com.javamagician.paquetes.dominio.ElementoPorHacer>();
        }
        elementosPorHacer.add(elementoPorHacer);
    }

    // getters y setters estándar
}

5. Compilación con javac

Cuando es hora de compilar nuestras clases empaquetadas, debemos recordar nuestra estructura de directorios. Comenzando en la carpeta de origen, debemos indicar a javac dónde encontrar nuestros archivos.

Primero, debemos compilar nuestra clase com.javamagician.paquetes.dominio.ElementoPorHacer porque nuestra clase ListaPorHacer depende de ella.

Comencemos abriendo una línea de comandos o terminal y navegando hasta nuestra carpeta de origen.

Ahora, compilamos nuestra clase com.javamagician.paquetes.dominio.ElementoPorHacer:

> javac com/javamagician/paquetes/dominio/ElementoPorHacer.java

Si nuestra clase se compila correctamente, no veremos mensajes de error y debería aparecer un archivo ElementoPorHacer.class en nuestro directorio com/javamagician/paquetes/dominio.

Para los tipos que hacen referencia a tipos en otros paquetes, debemos usar la "flag" -classpath para indicar a la comando javac dónde encontrar las otras clases compiladas.

Ahora que nuestra clase ElementoPorHacer está compilada, podemos compilar nuestras clases ListaPorHacer y AppPorHacer:

> javac -classpath . com/javamagician/paquetes/*.java

Nuevamente, no deberíamos ver mensajes de error y deberíamos encontrar dos archivos de clase en nuestro directorio com/javamagician/paquetes.

Ejecutemos nuestra aplicación usando el nombre completamente calificado de nuestra clase AppPorHacer:

> java com.javamagician.paquetes.AppPorHacer

Nuestra salida debería verse así:

ElementoPorHacer [id=0, descripcion=Elemento por hacer 1, fecha=2023-09-18]
ElementoPorHacer [id=1, descripcion=Elemento por hacer 2, fecha=2023-09-18]
ElementoPorHacer [id=2, descripcion=Elemento por hacer 3, fecha=2023-09-18]

6. Conclusión

En este breve artículo, aprendimos qué es un paquete y por qué deberíamos usarlos.

Discutimos las convenciones de nomenclatura y cómo se relacionan los paquetes con la estructura de directorios. También vimos cómo crear y usar paquetes.

Finalmente, repasamos cómo compilar y ejecutar una aplicación con paquetes utilizando los comandos javac y java.