W3docs

Package Java

Raggruppa le classi Java in package, segui le convenzioni di naming e struttura i progetti per la manutenibilità.

Java non ha un unico grande pool di nomi di classe. Ogni classe vive all'interno di un package — uno spazio con nome che funziona sia come unità di organizzazione sia come namespace a livello Java. Due classi chiamate Logger possono coesistere senza conflitti finché si trovano in package diversi, e il nome del package compare ovunque: nelle istruzioni import, nei nomi completamente qualificati, nel file system, persino nei manifest JAR. Una buona conoscenza dei package è ciò che ti permette di leggere la struttura di un progetto altrui a colpo d'occhio.

Questa pagina tratta cosa sia un package, come denominarne uno, il caso speciale del package predefinito, come i nomi dei package si mappano sulle directory e come dichiarare un package nel proprio file sorgente.

Cosa è effettivamente un package

Un package serve tre scopi contemporaneamente:

  • Un namespace. java.util.Date e java.sql.Date sono classi diverse; il nome del package le tiene separate.
  • Un confine di accesso. Senza un modificatore, i membri sono visibili solo all'interno dello stesso package — il livello di accesso "package-private". È una forma reale e strutturale di incapsulamento; vedi Modificatori di accesso.
  • Una directory. Il nome del package si mappa uno a uno su un percorso di cartella. com.example.app.util si trova in com/example/app/util/.

Lo stesso nome viene usato in tre punti — dichiarazione, percorso del file e import — e devono tutti corrispondere.

Convenzioni di naming

La convenzione di Java è il naming in DNS inverso basato su un dominio che controlli:

  • Tutto in minuscolo: com.example, non Com.Example.
  • Ordine di dominio inverso: un progetto su w3docs.com usa com.w3docs come radice.
  • I segmenti di progetto, modulo e funzionalità seguono: com.w3docs.learnjava.parser.
  • Evita le parole riservate di Java come segmenti (int, class, new). Se il tuo dominio ne include una, modificala: com.example.int_ o suddividi diversamente.

Queste convenzioni contano oltre l'estetica. La regola DNS inverso è ciò che rende sicuro inserire JAR di organizzazioni diverse nel medesimo classpath senza conflitti di nomi.

Il package predefinito

Un file .java senza dichiarazione package appartiene al package predefinito (senza nome). Di conseguenza accadono due cose:

  • Non puoi fare import dal package predefinito in uno nominato. Tutto ciò che si trova in esso è praticamente isolato dal codice reale.
  • Gli strumenti di build, gli IDE e i sistemi di moduli trattano il package predefinito come un caso degenere — molti rifiutano categoricamente di compilare contro di esso.

Usalo per file Hello.java occasionali. Non distribuire nulla da esso.

Come i package si mappano sulle directory

Se dichiari package com.w3docs.learnjava.parser; in cima a Tokenizer.java, il file deve trovarsi in:

com/w3docs/learnjava/parser/Tokenizer.java

relativo alla source root. Il compilatore non deduce il package dal percorso — legge la dichiarazione e si fida di te. Ma il runtime (e la maggior parte degli strumenti) non sarà contento se i due non coincidono.

Quella source root è dove inizia il percorso del classpath: la JVM deve sapere dove inizia l'albero dei package, altrimenti non trova nulla.

Dichiarare un package

L'istruzione package è ciò che assegna una classe a un package. Deve essere la prima istruzione nel file — prima di qualsiasi import, prima della classe stessa. Solo commenti e righe vuote possono precederla.

// File: com/w3docs/learnjava/parser/Tokenizer.java
package com.w3docs.learnjava.parser;

import java.util.List;

public class Tokenizer {
    public List<String> tokenize(String source) {
        // ...
        return List.of();
    }
}

Da qualsiasi altro punto, questa classe è ora nota con il suo nome completamente qualificato, com.w3docs.learnjava.parser.Tokenizer. Il codice nello stesso package può riferirsi ad essa semplicemente come Tokenizer; il codice in altri package o la importa o scrive il nome completo.

Attenzione
Un file può contenere al massimo un'istruzione package, e solo una classe public di primo livello — il cui nome deve corrispondere al nome del file. Metti package in qualsiasi posizione diversa dalla cima e il compilatore rifiuterà il file.

Un esempio pratico: due Logger

L'argomento più chiaro a favore dei package è il conflitto che prevengono. Il seguente programma usa due classi chiamate Logger in senso figurato — il java.util.logging.Logger del JDK con il suo nome completamente qualificato — e mostra come il package di una classe diventi parte della sua identità a runtime.

java— editable, runs on the server

Due conclusioni dall'esecuzione: le classi conoscono il proprio package a runtime (tramite Class.getName() e Class.getPackage()), e il nome completamente qualificato è ciò che identifica un tipo in modo univoco — Logger da solo è ambiguo; java.util.logging.Logger non lo è.

Cosa c'è dopo

Denominare un package è una cosa; portare i suoi tipi nel proprio codice è un'altra. Il capitolo successivo tratta l'istruzione import — import di tipo singolo, import con wildcard, import statici e quando ciascuno è la scelta giusta.

Esercizio

Pratica
Perché la convenzione standard di Java per i nomi dei package usa l'ordine di dominio inverso, come `com.example.app`?
Perché la convenzione standard di Java per i nomi dei package usa l'ordine di dominio inverso, come `com.example.app`?
Was this page helpful?