Credo che ci siano due usi correlati del canonico: forme e istanze.
Una forma canonica significa che i valori di un particolare tipo di risorsa possono essere descritti o rappresentati in più modi e uno di questi modi viene scelto come forma canonica preferita. (Quella forma è canonizzata , come i libri che sono entrati nella Bibbia, e le altre forme no.) Un classico esempio di una forma canonica sono i percorsi in un file system gerarchico, in cui un singolo file può essere referenziato in diversi modi :
myFile.txt # in current working dir
../conf/myFile.txt # relative to the CWD
/apps/tomcat/conf/myFile.txt # absolute path using symbolic links
/u1/local/apps/tomcat-5.5.1/conf/myFile.txt # absolute path with no symlinks
La definizione classica della rappresentazione canonica di quel file sarebbe l'ultimo percorso. Con percorsi locali o relativi non è possibile identificare globalmente la risorsa senza informazioni contestuali. Con i percorsi assoluti puoi identificare la risorsa, ma non puoi dire se due percorsi si riferiscono alla stessa entità. Con due o più percorsi convertiti nelle loro forme canoniche, puoi fare tutto quanto sopra, inoltre determinare se due risorse sono uguali o meno, se questo è importante per la tua applicazione (risolvi il problema di aliasing ).
Si noti che la forma canonica di una risorsa non è una qualità di quella particolare forma stessa; ci possono essere più forme canoniche possibili per un dato tipo come percorsi di file (diciamo, lessicograficamente prima di tutto possibili percorsi assoluti). Una forma viene selezionata come forma canonica per un particolare motivo di applicazione, o forse arbitrariamente in modo che tutti parlino la stessa lingua.
Forzare gli oggetti nelle loro istanze canoniche è la stessa idea di base, ma invece di determinare una rappresentazione "migliore" di una risorsa, sceglie arbitrariamente un'istanza di una classe di istanze con lo stesso "contenuto" del riferimento canonico, quindi converte tutti i riferimenti a oggetti equivalenti per utilizzare l'unica istanza canonica.
Questo può essere usato come tecnica per ottimizzare sia il tempo che lo spazio. Se in un'applicazione sono presenti più istanze di oggetti equivalenti, costringendoli a risolverli tutti come singola istanza canonica di un determinato valore, puoi eliminare tutti tranne uno di ciascun valore, risparmiando spazio e possibilmente tempo poiché ora puoi confrontare quei valori con identità di riferimento (==) in contrasto con l'equivalenza dell'oggetto ( equals()metodo).
Un classico esempio di ottimizzazione delle prestazioni con istanze canoniche è la compressione di stringhe con lo stesso contenuto. La chiamata String.intern()su due stringhe con la stessa sequenza di caratteri è garantita per restituire lo stesso oggetto String canonico per quel testo. Se passi tutte le tue stringhe attraverso quel canonicalizer, sai che le stringhe equivalenti sono in realtà riferimenti a oggetti identici, cioè alias
I tipi enum in Java 5.0+ costringono tutte le istanze di un particolare valore enum a utilizzare la stessa istanza canonica all'interno di una VM, anche se il valore è serializzato e deserializzato. Questo è il motivo per cui puoi usare if (day == Days.SUNDAY)impunemente in java if Daysè un tipo enum. Fare questo per le tue classi è certamente possibile, ma è necessario. Leggi Effective Java di Josh Bloch per dettagli e consigli.