Packages

Packages

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 dentro de ellos.

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

Finalmente, compilaremos y ejecutaremos nuestras clases Java empaquetadas.

2. Visión general de los paquetes en Java

En Java, utilizamos paquetes para agrupar clases, interfaces y subpaquetes relacionados.

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 llamadas com.baeldung.Application y com.example.Application.
  • Controlar el acceso: podemos controlar la visibilidad y el acceso a los 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.baeldung.packages:

package com.baeldung.packages;Copy

Se recomienda encarecidamente colocar cada nuevo tipo en un paquete. Si definimos tipos y no los colocamos en un paquete, irán al paquete predeterminado 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 del paquete predeterminado desde otros paquetes.
  • Los alcances de acceso protected y package-private serían irrelevantes.

Como lo establece 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 al comenzar el desarrollo.

Por lo tanto, deberíamos evitar usar paquetes sin nombre o predeterminados en aplicaciones del mundo real.

3.1. Convenciones de nomenclatura

Para evitar conflictos de nombres entre paquetes, seguimos algunas convenciones de nomenclatura:

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

Para determinar el nombre del paquete basado en una organización, típicamente comenzaremos invirtiendo 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 www.baeldung.com, vamos a invertirlo:

com.baeldungCopy

Luego podemos definir subpaquetes de este, como com.baeldung.packages o com.baeldung.packages.domain.

3.2. Estructura de directorios

Los paquetes en Java corresponden con una estructura de directorios.

Cada paquete y subpaquete tiene su propio directorio. Por lo tanto, para el paquete com.baeldung.packages, deberíamos tener una estructura de directorios de com -> baeldung -> packages.

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

4. Uso de miembros del paquete

Comencemos por definir una clase TodoItem en un subpaquete llamado domain:

package com.baeldung.packages.domain;

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

    // getters y setters estándar
}

4.1. Importaciones

Para usar nuestra clase TodoItem desde una clase en otro paquete, necesitamos 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 domain:

import com.baeldung.packages.domain.*;Copy

Ahora, importemos solo la clase TodoItem:

import com.baeldung.packages.domain.TodoItem;Copy

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 y la clase ArrayList del núcleo de Java:

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

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

public class TodoList {
    private List<TodoItem> todoItems;

    public void addTodoItem(TodoItem todoItem) {
        if (todoItems == null) {
            todoItems = new ArrayList<TodoItem>();
        }
        todoItems.add(todoItem);
    }
}

Aquí, hemos utilizado nuestras nuevas clases junto con las clases del núcleo de Java para crear una List de ToDoItems.

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 tanto java.sql.Date como 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 TodoItem con un nombre completamente calificado:

public class TodoList {
    private List<com.baeldung.packages.domain.TodoItem> todoItems;

    public void addTodoItem(com.baeldung.packages.domain.TodoItem todoItem) {
        if (todoItems == null) {
            todoItems = new ArrayList<com.baeldung.packages.domain.TodoItem>();
        }
        todoItems.add(todoItem);
    }
    // getters y setters estándar
}

Compilación con javac

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

Necesitamos compilar nuestra clase TodoItem primero porque nuestra clase TodoList depende de ella.

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

Ahora, compilamos nuestra clase com.baeldung.packages.domain.TodoItem:

> javac com/baeldung/packages/domain/TodoItem.javaCopy

Si nuestra clase se compila correctamente, no veremos mensajes de error y aparecerá un archivo TodoItem.class en nuestro directorio com/baeldung/packages/domain.

Para tipos que hacen referencia a tipos en otros paquetes, debemos usar la bandera -classpath para indicarle al comando javac dónde encontrar las otras clases compiladas.

Ahora que nuestra clase TodoItem está compilada, podemos compilar nuestras clases TodoList y TodoApp:

> javac -classpath . com/baeldung/packages/*.javaCopy

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

Ejecutemos nuestra aplicación utilizando el nombre completamente calificado de nuestra clase TodoApp:

> java com.baeldung.packages.TodoAppCopy

Nuestra salida debería lucir así: