W3docs

Creare Package Personalizzati in Java

Crea i tuoi package Java: struttura delle directory, dichiarazione package e come compilarli e utilizzarli.

Leggere i package altrui è una cosa. Crearne uno proprio richiede solo tre piccoli passi: scegliere un nome, organizzare le directory e aggiungere la corretta dichiarazione package in cima a ogni file. Il compilatore e la JVM seguono una convenzione rigorosa, e dopo averla applicata un paio di volte diventa automatica. Se sei alle prime armi con il concetto di package, inizia dalla panoramica sui package Java; questo capitolo si concentra sulla creazione dei tuoi.

Le tre regole

Tutto ciò che scrivi all'interno di un package deve rispettare esattamente tre regole:

  1. La prima istruzione non-commento del file è la dichiarazione package. Nessun'altra istruzione può precederla.
  2. Il percorso del file rispecchia il nome del package. com.example.util.Strings deve trovarsi in com/example/util/Strings.java sotto una radice sorgente.
  3. Il file prende il nome dalla sua classe pubblica di primo livello. Strings.java per public class Strings.

È tutto. Il resto è convenzione.

Scegliere un nome per il package

Il compilatore applica solo le tre regole precedenti, ma alcune convenzioni di denominazione mantengono i tuoi package privi di conflitti e idiomatici:

  • Tutto in minuscolo. com.w3docs.greet, mai com.W3Docs.Greet. Le lettere maiuscole nei nomi dei package funzionano tecnicamente, ma violano la convenzione che ogni sviluppatore Java si aspetta, e causano problemi sui file system case-insensitive.
  • Inverti il tuo nome di dominio. Se possiedi w3docs.com, i tuoi package iniziano con com.w3docs. Questo garantisce unicità globale, così il tuo Greeter non entrerà mai in conflitto con il Greeter di qualcun altro nel classpath.
  • Nessuna keyword Java. Un segmento di package non può essere una parola riservata. Se il tuo dominio fosse int.example.com, non potresti scrivere com.example.int — dovresti scriverlo come com.example.int_.
  • Nessuna cifra all'inizio di un segmento. com.3m.tape non è valido perché 3m non è un identificatore valido; una soluzione comune è com._3m.tape.

Se non dichiari alcun package, le tue classi finiscono nel package predefinito — va bene per uno snippet veloce, ma le classi lì non possono essere importate da codice in package, quindi evitalo nei progetti reali.

Un esempio minimale

Supponiamo di voler creare un piccolo package di utilità chiamato com.w3docs.greet. Ecco la struttura:

project/
└── src/
    └── com/
        └── w3docs/
            └── greet/
                ├── Greeter.java
                ├── Greeting.java
                └── Main.java

Greeting.java:

package com.w3docs.greet;

public record Greeting(String language, String text) {}

Greeter.java:

package com.w3docs.greet;

public class Greeter {
  public Greeting greet(String name) {
    return new Greeting("en", "Hello, " + name + "!");
  }
}

Main.java:

package com.w3docs.greet;

public class Main {
  public static void main(String[] args) {
    System.out.println(new Greeter().greet("Ada").text());
  }
}

Tutti e tre i file iniziano con la stessa riga package com.w3docs.greet;. Poiché condividono un package, Greeter e Main possono usare Greeting senza alcun import — si usa import solo per i tipi di altri package.

Compilare ed eseguire dalla riga di comando

Dalla radice project/, i comandi canonici sono:

# Compile every .java file under src/ into a parallel tree under out/
javac -d out $(find src -name "*.java")

# Run a main class by its fully-qualified name, telling the JVM where out/ is.
java -cp out com.w3docs.greet.Main

-d out indica a javac dove mettere i file .class compilati; preserva la struttura di directory del package. -cp out (classpath) è il modo in cui java li trova a runtime — il prossimo capitolo sul classpath Java entra nei dettagli.

Se la dichiarazione del package non corrisponde alla posizione del file relativa alla radice sorgente, javac la accetta (il package è quello dichiarato, non quello inferito dal percorso) — ma java fallirà a runtime con NoClassDefFoundError. Mantieni sempre i due in sincronia.

I sotto-package non sono annidati

È tentante pensare che com.example "contenga" com.example.util, ma per il compilatore Java sono due package non correlati che condividono per caso un prefisso nel nome. Una classe in com.example non ha accesso speciale ai membri package-private di com.example.util. Non esiste ereditarietà tra package.

Ciò che i sotto-package effettivamente offrono è un albero di directory facile da navigare e un posto logico per raggruppare il codice correlato. La maggior parte dei progetti reali usa sotto-package per funzionalità (auth, billing, parser) o per layer (controller, service, repository) — ma nessuna delle due scelte conferisce al sotto-package un accesso aggiuntivo.

Unità di compilazione e package-info.java

Un file .java è detto anche unità di compilazione, e può contenere:

  • Una dichiarazione package (oppure nessuna, per il package predefinito).
  • Un numero qualsiasi di dichiarazioni import.
  • Un numero qualsiasi di dichiarazioni di tipo, di cui al massimo una può essere public, e quella deve corrispondere al nome del file.

Esiste anche un file speciale chiamato package-info.java il cui unico scopo è portare Javadoc e annotazioni a livello di package:

/**
 * Greeting utilities for the W3Docs Java book.
 */
@NullMarked
package com.w3docs.greet;

Nessun tipo — solo il commento, eventuali annotazioni e la riga package. Lo troverai nelle librerie ben documentate.

Un esempio pratico

Questo programma dichiara due tipi che in un progetto reale vivrebbero nello stesso package e li usa insieme. Il widget eseguibile appiattisce la struttura dei file in modo da mostrare il collegamento in un unico sorgente, ma in un progetto reale ogni tipo pubblico vivrebbe nel proprio file .java secondo la struttura di directory mostrata sopra.

java— editable, runs on the server

Cosa c'è dopo

I package personalizzati coprono il codice che scrivi tu stesso. La maggior parte delle volte, però, farai affidamento sui package del JDK — le classi della libreria standard per collezioni, I/O, date e così via. Il prossimo capitolo è una guida ai quelli che userai più spesso. Continua con i package built-in di Java.

Esercitati

Pratica
Un file sorgente Java dichiara `package com.example.util;` ma viene compilato e poi eseguito da un `.class` situato in `out/com/example/Strings.class`. Cosa succede?
Un file sorgente Java dichiara `package com.example.util;` ma viene compilato e poi eseguito da un `.class` situato in `out/com/example/Strings.class`. Cosa succede?
Was this page helpful?