MokaPackages 
I packages del JDK
Introduzione
di
Piergiuseppe
Spinelli
Una analisi dettagliata di tutti i vari packages del JDK

 



Ecco un  nuova rubrica dedicata ai nuovi programmatori che si avvicinano a Java, una serie di articoli vogliamo dare una panoramica sulle classi standard del JDK 1.2 (ribattezzata Java 2). Faremo riferimento solo alle calssi più importanti della versione 1.2, evitando di soffermarci sulle classi ed i metodi dichiarati obsoleti e mantenuti per compatibilità con le precedenti versioni


Introduzione

La documentazione ufficiale fornita da SUN è molto migliorata nel JDK1.2 ma, comunque, si presenta come un reference e non come un manuale d’uso. Con questa rubrica, quindi, non ci poniamo scopi di completezza o super-approfondimento (per quanto contiamo di dare qualche dritta dove capita); vogliamo, piuttosto, tracciare una mappa che consenta di navigare nell'oceano di classi e trovare rapidamente ciò che meglio si adatta ai nostri bisogni, che siano di calcolo, di comunicazione o di sicurezza, che richiedano di macinare stringhe o file, immagini o animazioni, compressioni o encriptaggi e, senza mettere limiti alla provvidenza, provare a scendere anche nei meandri dei domini di uso meno frequente e delle estensioni standard.
Questo primo articolo, prima di entrare nel vivo del package Java più usato, java.lang, presenta una breve panoramica sull’uso delle librerie nei moderni linguaggi di programmazione. Si tratta di argomenti noti a molti ma che potrebbero aiutare qualche neofita di Java, magari già esperto di altri linguaggi come BASIC e C, ad inquadrare meglio l’argomento di questa nuova serie.

     
Dalla funzione al componente: linguaggi General-Purpose e Librerie di Funzioni
Inizialmente i linguaggi di terza generazione (FORTRAN, COBOL, BASIC, Pascal, etc…) non avevano una chiara linea di demarcazione tra linguaggio ed operazioni su specifici tipi di dati: tutto veniva fornito in un unico calderone sotto forma di parole del linguaggio; così nessuna distinzione era fatta tra print, shell, if, mid$, etc…
Questo brodo primordiale si rivelò subito poco maneggievole e, ben presto, vennero fatte distinzioni tra le strutture del linguaggio (if, for, while etc…), dati di base (p.e. integer, string, float) e dati ed operazioni specifici di determinati domini (records, tipi utente, funzioni inerenti un certo tipo di dato).
In particolare il C Language modificò il modo di pensare dei programmatori che subito capirono la comodità di avere un linguaggio minimalista, che implementasse solo le strutture ed i paradigmi di base (puntatori, array, funzioni, regole di visibilità, dichiarazioni di tipo, etc…) ed i tipi di dati macchina, ovvero corrispondenti a quelli supportati direttamente dall’hardware (interi, floating point, caratteri ed indirizzi). Si diffuse così il concetto di Function Library (in inglese biblioteca ma ormai universalmente tradotto come libreria di funzioni): con qualche difficoltà iniziale dei programmatori BASIC o Pascal, bisognava decidere i domini dei dato di interesse per ogni applicazione, includere gli specifici file di definizione (headers) e linkare le specifiche librerie.
Le funzioni venivano catalogate in modo che il programmatore non fosse costretto a linkare troppe funzioni non usate ma, al tempo stesso, non dovesse muoversi nella frammentazione di migliaia di librerie (solo in un secondo tempo i linker sarebbero diventati abbastanza intelligienti da scartare le funzioni non utilizzate). I criteri adottati per tale categorizzazione erano fondamentalmente due:
    Frequenza d’uso
    Dominio comune di dato
Venne individuato un insieme di operazioni, per quanto non incluse nel linguaggio, praticamente utilizzate in tutti i programmi: tali funzioni furono raggruppate in librerie standard: in particolare stdlib e stdio (per il C) fornivano da sole una potenza di fuoco quasi pari a quella dei linguaggi di tipo BASIC.
Le altre librerie raccoglievano operazioni accumunate dall’avere per oggetto lo stesso tipo di dato (o un piccolo insieme di tipi correlati). Esempi di questo tipo sono math, string, file. A pensarci adesso, fu anche merito di questo tipo di raggruppamento se si fece largo, al di fuori degli ambienti accademici, l’idea di Oggetto, o megglio di Classe, intesa come definizione di tipo di dato comprensivo dell’insieme di operazioni (metodi) consentiti su di esso.
Classi, Package e Framework
Tutti i linguaggi ad oggetti sono, più o meno consciamente (per molti sarà un’eresia, ma tant’è), discendenti dalle filosofia dei moderni linguaggi General-Purpose. Ciò che intendo dire è che essi implementano i soli meccanismi di base, i tipi di dato elementari, le strutture di controllo e, in modo non dissimile dal buon vecchio linguaggio C, demandano le operazioni effettive a specifiche librerie di dominio, non Function Library, in questo caso, ma Class Library. Non solo C++, Objective-C o Java, che sono chiari eredi del C anche sotto il punto di vista sintattico, ma persino Smalltalk o Eifel, in fondo, hanno alla base questa impostazione.
In realtà le librerie di classi, partendo da tale base, vanno molto più in là. Se package Java come Math o classi come String possono essere visti come i corrispettivi delle omonime librerie C, altre, come Observer ed Observable, costituiscono delle alleaze allo scopo di implementare determinati patterns di uso frequente e non sono, quindi, accumunate dei medesimi dati ma, piuttosto, da un scopo comune. Tralasciando le varie liberie (STL, MFC, etc…) del C++ e limitandoci a Java, troviamo dei mastodontici esempi del genere in alcuni package (collezioni di classi java correlate: utilizzabile come sinonimo di Libreria di Classi). In particolare AWT e Swing: ci riferiamo a librerie di questo genere come framework.
Mentre le comuni Class library possono essere considerate come tools disponibili al bisogno per fare determinati lavori durante una elaborazione il cui corso viene definito dal programma che ne fa uso, i framework hanno un’impostazione esattamente opposta.
Quando creiamo, con Swing, una finestra con un menù, non scriviamo una riga di codice per programmare il loro comportamento; al massimo riscriviamo un metodo virtuale già esistente per modificare alcuni comportamenti standard predefiniti (tipicamente le azioni corrispondenti alle varie voci di menù). Il nostro approccio diviene, in buona parte, dichiarativo:
Definisco un’applicazione che è una finestra
Definisco un menù dentro la finestra
Al limite, non c’è bisogno d’altro. Il programma viene eseguito e la finestra si comporta come ci si attende debba comportarsi una finestra per bene, il menù si aprirà sotto al cursore ed eseguirà le azioni predefinite (normalmente azioni nulle). La differenza tra un framework ed una comune libreria di classi (per quanto entrambi siano definiti internamente a dei package) è quindi la seguente: le semplici librerie sono passive (implementano comportamenti su richiesta dei programmi) mentre i framework sono attivi (reagiscono in modo generico agli eventi esterni e si aspettano che i programmi si configurino come specializzazioni del comportamento standard da essi fornito).
Componenti, Collezioni, Object Model e Scripting
Per concludere, la new wave della programmazione, o se preferiamo, l’estrema conseguenza della filosofia ad oggetti, è data dal concetto di componente. Un componente è un insieme di classi che presenta alcune interfacce standard che consentono a vari linguaggi (anche diversi da quelli utilizzati per scrivere il componente) e tool visuali di navigare attraverso i metodi e le proprietà, scoprire i tipi di parametri e le convenzioni utilizzate e, infine, utilizzarli quando necessario.
Alla base dei componenti ci sono alcuni meccanismi determinanti. Prima di tutto quelli di interfaccia e di link dinamico: è importante che il sistema di sviluppo (p.e. VisualBasic o VisualAge) riconosca alcuni protocolli comuni con i componenti che utilizza. Ogni componente viene poi caricato in modo dinamico ed implementa, in modo diverso, i metodi definiti nel protocollo.
Prendiamo ad esempio un ipotetico componente per la gestione della stampa: chiamiamolo printing.
La sua interfaccia standard di componente (JavaBeans in Java, IUnknow per ActiveX, etc…) consente l’accesso alle proprietà ed ai metodi da parte del tool di programmazione. Una possibile realizzazione è la seguente (metodi in verde, proprietà in rosso):
    Printing:
       
      printers (collection of Printer)
      add
      count
      remove
      get
      setDefaultPrinter
      getDefaultPrinter
    Printer:
       
      type
      print
      newPage
      setLoader
      setDoubleSide
      showConfigurationWindow
Non tutti i componenti presentano l’intero insieme di interfacce: solo le stampanti laser consentono la scelta del cassetto di caricamento mentre solo quelle predisposte per il fronte retro avranno metodi per l’attivazione di tale opzione.
L’esempio presenta la tipica struttura di un componente:
    un’interfaccia d’accesso (sottointesa nell’esempio)
    uno o più oggetti radice (in questo caso printing)
    alcune proprietà (p.e. printers) sono definite come collezioni di altri oggetti (in altre parole fungono da punto di accesso per oggetti contenuti).
    gli oggetti contenuti (p.e. Printer) possono essere interrogati per la loro definizione attraverso la medesima interfaccia dei componenti e, a loro volta, possono contenere altre collezioni di oggetti.
Si tratta, quindi, di una struttura gerarchica formata da oggetti che contengono collezioni ad altri oggetti e così via. Questa rappresentazione classica ad albero, che viene utilizzata comunemente per documentare la struttura di un componente, viene detta Object Model.
La forza di questo paradigma sta nel fatto che grandi applicazioni possono essere scomposte in un certo numero di componenti che interagiscono tra loro ed essere, quindi, descritte tramite uno specifico Object Model. In questo modo è possibile scrivere programmi che automatizzano il comportamento di altri programmi. A tale scopo si sono affermati linguaggi semplificati (p.e. senza type checking), detti di Scripting, facilmente utilizzabili dagli utenti finali (magari appena un po’ evoluti) che permettono di comporre pezzi di programmi diversi per nuovi scopi. Così, mentre voi professionisti sgobbate e scavate nei meandri del three tiers, degli Application Server ed altre amenità, il vostro collega dell’amministrazione mette insieme, in mezza giornata, un grafico Excel ed una form HTML, scrive due righe in JavaScript e schiaffa il tutto su una pagina INTRANET che permette ai vari manager di calcolarsi al volo qualche dato di vitale importanza, guadagnando il plauso generale e qualche segreto anatema da parte vostra.
Package di base
Se avete programmato in C, sarà diventato un automatismo quello di cominciare i vostri sorgenti con

#include <stdio.h>
#include <stdlib.h>
o, in ambiente Windows, con

#include <windows.h>
Ogni tentativo di scrivere programmi C senza l’inclusione di librerie atte ad interfacciarsi con il sistema operativo ospite è votato all’insuccesso (almeno che non siate esperti nel produrre software di base).
Se invece avete più dimestichezza con il VisualBasic, troverete normale cominciare a scrivere una subroutine dichiarando variabili di vari tipi, anche non elementari come collection e control, e immediatamente agire su di esse tramite i relativi metodi. Se, però, dovete usare componenti più complessi, come ad esempio un grafico Excel, siete obbligati ad andare nell’apposita lista dei references per includere le classi necessarie. Nel fare questa operazione scoprirete di avere alcuni reference già collegati implicitamente dal sistema, alcuni addirittura obbligatori. Ecco svelata la magia delle classi standard di VB: sono anch’esse delle librerie esterne ma collegate per default.
In Java accade qualcosa di molto simile: il compilatore, javac, provvede al link automatico del package java.lang. Nulla ci impedisce di dichiarare l’uso di tale package in modo esplicito, con:

import java.lang.*;
Anche se tale dichiarazione viene omessa, Java non può fare a meno delle classi contenute in tale package in quanto, molte di esse, sono da supporto ai meccanismi del linguaggio. Basti pensare che, per definizione, tutte le classi Java derivano da Object che è definita in java.lang.
Per quanto riguarda la grana delle sue librerie, Java è assai più modulare di VisualBasic e, per poter disporre di un volume di funzionalità paragonabile a quello della sola libreria standard VB, è necessario dichiarare, come minimo, i package java.util, java.io, java.math etc… Se, inoltre, i nostri programmi devono disporre di un’interfaccia grafica, sara necessario ricorrere ad uno o più package, scegliando ad esempio tra java.awt, e java.swing; già la possibilità di fare questo tipo di scelte rappresenta un vantaggio rispetto al VB che, invece, deve portarsi appresso forzatamente un bagaglio standard tutt’altro che leggero.
Proseguendo con i paragoni, sia Java che VB collegano le proprie librerie in modo dinamico, potendo scegliere i componenti effettivi da usare anche a run-time. Tuttavia Java estrae dai suoi package le sole classi effettivamente utilizzate mediante un meccanismo di indirizzamento basato sui nomi e, fondamentalmente, simile a quello che consente di rintracciare archivi in un file system a partire dalla radice; VB, invece, effettua il collegamento con ogni classe in base ad un ID univoco che deve essere memorizzato nel registry di Windows. Probabilmente al fine di non sovraccaricare il registy con un’infinità di entry, la grana dei componenti collegati è solitamente abbastanza grande (spesso pari a diverse decine di classi) e, di conseguenza, un programma VB è spesso costretto ad inglobare un gran numero di funzionalità superflue ai suoi scopi.
Al termine di questa panoramica, che spero sia stata di qualche utilità per i Java Beginners prevenienti da altri ambienti di sviluppo, iniziamo a dare un’acchiata ai tre package che ogni programmatore Java, indipendentemente dal suo campo di interesse, dovrebbe conoscere in modo approfondito. 


MokaByte rivista web su Java
MokaByte ricerca nuovi collaboratori
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it