Sei sulla pagina 1di 12

Programmazione ad oggetti: classi ed oggetti

La programmazione ad oggetti un modo di organizzare i dati ed il codice dei programmi. Un oggetto una struttura in memoria che rappresenta una certa entit logica o fisica. Ogni oggetto composto da un insieme di attributi e metodi. Una classe la descrizione di tutti gli oggetti che appartengono ad una stessa classe. Una classe si dichiara specificandone il nome seguito dalla dichiarazionbe dei suoi attributi, il/i costruttore/i ed i metodi. Ognuno di questi elementi opzionale.
nome_classe { //attributi //costruttori //metodi }

Attributi
Gli attributi sono come delle variabili, visibili all'interno di tutta la classe. Quindi, un attributo si pu usare inizializzando altri attributi, nei costruttori e in tutti i metodi della classe. Inoltre, usato da chi fa uso di un oggetto di quella classe.

Costruttori
Un costruttore si dichiara con la sintassi: <nome_classe> ( <parametro1>, <parametro2>, ...) { <codice> } Serve ad "inizializzare" un oggetto. Generalmente si mette nel costruttore tutto il codice che serve per "preparare" un oggetto al suo uso; ad esempio, inizializzare i suoi attributi.

Istanziare
Data una descrizione di una classe, si possono istanziare oggetti di quella classe. In pratica, viene allocata della memoria (ram) per contenere i valori degli attributi di un oggetto. La sintassi per istanziare un oggetto : <nome classe> <nome_variabile> = new <costruttore> ; Come per i tipi primitivi, la variabile un nome che vogliamo noi.

Usare gli oggetti


Per accedere (leggere o scrivere) gli attributi di un oggetto si usa la sintassi: <variabile>.<attributo> Per usare i metodi di un oggetto si usa la sintassi: <variabile>.<metodo>

Le stringhe sono oggetti


Le variabili di tipo String che abbiamo usato fino ad ora sono riferimenti ad oggetti. Quando scriviamo:
String s = "ciao" ;

in realt viene tradotto in:


int l = s.lenght() ;

String s = new String("ciao") ;

La lunghezza della stringa si ottiene invocando un metodo della classe String:

Esempio: biblioteca
Ad esempio, vogliamo scrivere un programma per gestire i libri di una biblioteca. Programmando ad oggetti "libro" e "biblioteca" sono degli ottimi candidati per diventare delle classi. Un libro pu essere descritto da una classe con i seguenti attributi: autore, titolo, editore, anno, descrizione. Vedere biblioteca/Libro.java Una biblioteca invece contiene una collezione di libri ed i metodi per elencarli tutti e per cercare gli autori. Vedere biblioteca/Biblioteca.java Notare che nessuna delle due classi contiene un main. Per provare le classi creo una classe apposita con un main per provare le classi. Vedere biblioteca/ProvaBiblioteca.java

Uso della memoria


Ci sono due cose fondamentali da sapere sulla gestione della memoria in relazione agli oggetti.

Il valore NULL
Attenzione. Diversamente dai tipi primitivi, una variabile che rappresenta un riferimento ad un oggetto (dunque anche String) non ha un valore di default ma vale "NULL". Se dichiaro soltanto una variabile per un oggetto, ma non lo istanzio, non posso usarne gli attributi ed i metodi. Ad esempio, se eseguo il seguente codice:
Libro l ; l.titolo = "Eva Luna" ;

ottengo un errore di esecuzione: "Null Pointer Exception".

Il Garbage Collector
Come gi detto, gli oggetti vanno istanziati, cio bisogna allocare al memoria che usano. Ma come faccio a "deallocarli". In Java non esiste un'istruzione esplicita per fare questo (come il "distruttore" in C++); la Java Virtual Machine si occupa automaticamente di riconoscere quali sono gli oggetti che non vengono pi usati da nessuno e di liberare la memoria che occupano. Questo processo si chiama "Garbage Collection".

I membri statici
Le classi possono contenere attributi o metodi statici. Questi sono legati alla descrizione della classe, non agli oggetti di quella classe, duque possono essere usati anche senza istanziare oggetti. Infatti, i metodi che abbiamo usato fino ad ora erano tutti statici. Per dichiarare attirubit o metodi statici basta inserire la parola static prima del loro tipo. Gli attibuti statici vengono spesso usati per dichiarare valori costanti. Ad esempio:
Math.PI ;

e' un attributo statico della classe Math (nativa di Java) che rappresenta il valore PI greco. Un altro esempio tenere conto delle istanze di una classe che vengono create. Ad esempio:
public class StaticTest { /** Attributo statico usato per contare il numero di istanze. */ static int numOfInstances = 0 ; /** Costruttore che aumenta il contatore di istanze */ public StaticTest() { StaticTest.numOfInstances++ ; // oppure semplicemente // numOfInstances++ ; } /** Restituisce il numero di istanze fatte. */ public static int getNumOfInstances() { return numOfInstances ; } public void aNormalMethod() { // quello che volete ... }

In un altro metodo posso farmi dire quante istanze ci sono. Ad esempio:


// Istanzio un oggetto StaticTest s = new StaticTest() ; // mi faccio dire quanti ce ne sono System.out.println("Ci sono in giro "+StaticTest.getNumOfInstances()+" oggetti della classe StaticTest") ; // Uso l'oggetto come tutti gli altri s.aNormalMethod() ;

Overloading
Fare overloading vuol dire: "dichiarare metodi con lo stesso nome ma parametri diversi". Serve a poter dichiarare metodi con lo stesso nome. Questo utile perch generalmente il nome di un metodo associato al significato dell'operazione che esegue. L'overloading pu essere fatto sia sui metodi che sui costruttori.

Esempio
La classe Libro pu essere estesa aggiungendo un altro costruttore:
public Libro(String autore, String titolo) { this.autore = autore ; this.titolo = titolo ; } public Libro(String autore, String titolo, String editore, int anno, String descrizione) { this(autore, titolo) ; this.editore = editore ; this.descrizione = descrizione ; }

Si possono aggiungere altri metodi per impostare i dati rimanenti:


public void impostaDati(String editore, int anno) { this.editore = editore ; this.anno = anno ; } public void impostaDati(String editore, int anno, String descrizione) { this.editore = editore ; this.anno = anno ; this.descrizione = descrizione ; }

Oppure, si pu sfruttare un metodo gi esistente ed aggiungere funzionalit. Il metodo precedente si pu scrivere come:
public void impostaDati(String editore, int anno, String descrizione) { impostaDati(editore, anno) ; this.descrizione = descrizione ; }

Estensioni ed Ereditariet
L'ereditariet ' il metodo pi diffuso di riutilizzare codice gi fatto nella programmazione ad oggetti. Estendere una classe vuol dire creare una nuova classe aggiungendo attributi e metodi ad una classe gi esistente. Si dice che la nuova classe eredita le funzionalit della prima. Attenzione. I costruttori NON vengono ereditati; bisogna ridefinirli tutti.

Super
La parola chiave super serve a fare riferimento a membri della sovraclasse o sopraclasse o superclasse.

Esempio
Vedere LibroCatalogato.java e VolumeLibro.java

Rappresentazione grafica
Spesso si vedono documentazioni su programmi ad oggetti che visualizzano l'organizzazione tra le classi in questo modo: Libro

LibroCatalogato

VolumeLibro

La classi vengono rappresentate con rettangoli. Le estensioni vengono rappresentate con frecce che vanno dalla sottoclasse alla classe estesa. Viene a crearsi una gerarchia di classi.

Polimorfismo
Il polimorfismo una conseguenza dell'estensione delle classi. Ci troviamo ad evere un insieme di tipi (ex Libro, LibroCatalogato, VolumeLibro) che sono tra loro in qualche modo relazionati. Gli oggetti di una sottoclasse possono essere trattati come se fossero oggetti della superclasse. Questo si pu fare perch ho la certezza di trattare un oggetto che ha ereditato tutti gli attributi e metodi di una superclasse. In pratica ho la possibilit di trattare indistintamente diversi tipi di oggetti, appartenenti a diverse sottoclassi, come se fossero oggetti della superclasse. Polimorfismo significa che gli oggetti cambiano il loro comportamento pur mantenendo la stessa forma.

Esempio
La dichiarazione di un oggetto di una sottoclasse di libro, ex: sipotrebbe senza problemi scrivere cos:
VolumeLibro inferno = new VolumeLibro("Dante", "Divina Commedia (Inferno)", 1) ; Libro inferno = new VolumeLibro("Dante", "Divina Commedia (Inferno)", 1) ;

Da ora in poi l'oggetto "inferno" pu essere trattato come se fosse un'istanza di Libro, tanto ne possiede sicuramente tutti gli attributi e i metodi.

Upcasting
Il tipo di assegnameneto appena visto si chiama "upcasting", cio assegnare un oggetto ad una variabile di tipo di una classe superiore. L'upcasting fatto in automatico, come quando si assegna un float ad un double. Attenzione per. Una volta fatto l'assegnamento ad un tipo superiore, non posso pi accedere ad un membro della sottoclasse.

Downcasting
Il downcasting l'inverso dell'upcasting. Ex:
VolumeLibro vol1 = (VolumeLibro)inferno ;

Attenzione: un assegnamento che potrebbe dare errori a run-time. Ad esempio se faccio:


LibroCatalogato libro_22 = (LibroCatalogato)inferno ;

La compilazione funziona, ma ottengo una ClassCastException durante l'esecuzione.

Esempio
Nell'esempio della biblioteca, posso continuare ad usare la biblioteca per contenere libri di sottotipi diversi. Vedere ProvaBiblioteca2.java

Membri final
La parola chiave "final" impedisce ad un attributo o ad un metodo di essere ridefiniti nelle sottoclassi.

Overriding e Binding dinamico


I meccanismi di overriding e binding dinamico caratterizzano la programmazionead oggetti. Rispetto ad una programmazione tradizionale, questi meccanismi eliminano la necessit di "tempestare" i programmi di costrutti "switch" o "else-if". Questo facilita l'ampliamento dei programmi.

Overriding
Le sottoclassi possono ridefinire i metodi della loro superclasse. Questo si chiama overriding. In genere viene fatto perch estendendo una classe, alcuni metodi hanno bisogno di essere modificati, o riscritti affinch il loro significato rimanga coerente con il significato delle informazioni contenutte nella sottoclasse.

Esempio
Nelle classi LibroCatalogato.java e VolumeLibro.java il metodo getInformazioni() soggetto ad overriding sia nella classe LibroCatalogato che in VolumeLibro. La sua estensione stata necessaria per stampare le informazioni aggiuntive dei due sottotipi di Libro.

Binding Dinamico
Quando si invoca un metodo di una classe, il motore di esecuzione di un programma ad oggetti controlla di quale tipo esattamente un oggetto. Infatti, come detto prima, l'oggetto potrebbe essere anche di un sottotipo di quello dichiarato. Se il metodo stato ridefinito nelle sottoclassi (overriding), viene eseguita la versione "pi in basso" nella gerarchia delle classi.

Esempio
Nella classe Biblioteca non stato necessario modificare niente!!! Quando vengono richieste le informazioni sui libri contenuti nella biblioteca, il meccanismo di binding dinamico individua automaticamente che un oggetto del vettore non della classe Libri, ma di una sottoclasse, ed invoca il metodo della sottoclasse appropriata. Vedere ProvaBiblioteca2.java Questo funziona perch l'associazione (binding) tra il nome di un metodo e la classe a cui appartiene viene fatto a run-time (dinamicamente). Da cui il nome binding dinamico.

Classi astratte
Le classi astratte sono classi contenenti metodi astratti. I metodi astratti sono metodi di cui viene dichiarata sltanto l'intestazione, senza il corpo. Le classi astratte sono in genere create per preparare del codice ed impostarne l'ordine di esecuzione. Le classi astratte sono pensate per essere estese da chi ha bisogno di sfruttare tale codice. Si usa la parola chiave abstract per dichiarare una classe ed un metodo astratti.

Esempio
Per fornirvi il codice base che apre una finestra vi abbiamo dato il codice di una classe dentro il quale scrivere i vostri programmi. Invece sarebbe stato pi opportuno firnirvi una classe astratta con il codice gi fatto. Vedere FinestraBaseAstratta.java e MiaFinestra.java I vostri programmi grafici possono essere scritti estendendo la classe astratta ed implementando il metodo astratto "disegna()" opportunamente preparato. Notare che rispetto all'approccio precedente, in questo caso voi potete sfruttare del codice gi fatto senza bisogno del sorgente, basterebbe allegare una documentazione ben fatta. In realt i commenti ai metodi ed alla classe sono gi una documentazione esaustiva all'uso della classe. Pi avanti vedremo che esistono dei toos per generare automaticamente la documentazione a partire dai commenti.

Organizzare le classi in packages


Quando si fanno grosse applicazioni, che possono contenere migliaia di classi, di solito le si organizza in packages. Un package una collezioni di classi. I packages sono organizzati gerarchicamente, come i files e le directory. Infatti, ad ogni package cossisponde una directory che contiene i files .java delle classi di quel package. Le classi appartenenti ad un package devono avere la dichiarazione del package di appartenenza come prima linea del codice sorgente (commenti esclusi): package <nomePackage> ; Le classi che fanno uso di classi di altri packages devono importare la classi esterne con l'istruzione import:
import <nomepackage>.<nomeclasse> ;

se ci sono pi packages innestati, si nominano separati dal punto:


import <nomepackage>.* ;

import <nomepackage1>.<nomepackage2>....<nomepackageN>.<nomeclasse> ;

Si possono importare tutte le classi di un package usando l'asterisco:

Esempio
Le classi Rettangolo e Cerchio sono state messe nel package "forme". Vedere directory forme ed i files Retangolo.java e Cerchio.java La classe Graphics definita nel package java.awt:
import java.awt.Graphics ; import java.awt.* ;

Oppure, posso importare il Graphics insieme a tutte le classi del suo package con:

Sub-packages
I packages, come le directories, posso essere infinitamente innestati. Tuttavia, l'import non ricorsivo. Importare un package non vuol dire che importo tutti i sub-packages. Facendo:
import java.* ;

l'import NON va a cercare nel package awt, dunque NON importa la classe Graphics.

Compilazione
Per compilare le classi di un package devo posizionarmi nella directory base/root, fuori da tutti i packages. Dunque se nell'esempio della biblioteca io dovevo entrare nella directory "biblioteca" per compilare ed eseguire le classi, nel caso delle forme io DEVO stare fuori dalla directory "forme":
> javac forme\Forma.java > javac forme\Rettangolo.java > javac forme\ProvaForme.java

oppure

> javac forme\*.java

In Unix e Mac invece delle "\" (backslash) si usano le "/" (slash).

Esecuzione
Sempre posizionato fuori dalla directory del package, invoco una classe spacificandone il nome come negli import, cio usando il punto (".") per separare i nomi dei package e delle classi.
> java forme.ProvaForme

Interfacce
Le interfacce descrivono un elenco di metodi senza codice. Si possono considerare come delle classe astratte con tutti i metodi astratti e senza costruttori. Una classe implementa un'interfaccia se definisce tutti imetodi dichiarati nell'interfaccia. La sintassi :
interface <nomeInterfaccia> { // ... elenco metodi }

Interfacce e polimorfismo
In riferimento al polimorfismo, anche le interfacce dichiarano dei tipi nuovi. Dunque valgono tutti i fenomeni di polimorfismo, upcasting e downcasting descritti prima. Vedere il package "forme"

Rappresentazione grafica
Anche le interfacce vengono rappresentate graficamente con dei rettangoli. Per distinguere le interfacce dalle classi si pu scrivere il nome dell'interfaccia in "italic" invece di testo normale. L'esempio delle forme pu essere rappresentato cos. Forma

Rettangolo

Cerchio

Implementare pi interfacce
La cosa si fa interessante considerando che una classe pu estendere un'altra classe ed implementare pi interfacce contemporaneamente.
class <nomeClasse> extends <superClasse> implements <interfaccia1>, <interfaccia2>, ... , <interfacciaN> { ... }

Dunque si possono creare classi i cui oggetti sono contemporaneamente di pi tipi.

Estendere interfacce
Anche le interfacce si possono estendere tra di loro:
interface <nomeInterfaccia> extends <superInterfaccia> { // ... elenco nuovi metodi }

Visibilit o "Controllo di Accesso"


Quando si cominciano a sviluppare applicazioni con molte classi necessario decidere per ogni classe, e per ogni metodo di una classe, chi pu farne uso. Per esempio, una classe con molte funzionalit potrebbe essere molto complessa, e contenere molti attributi e metodi. Tuttavia, solo alcuni di questi attributi e metodi servono a chi vuole fare uso della classe. Dunque, alcuni metodi devono essere cisibili all'esterno, mentre altri devono restare invisibili se non a chi mantiene il codice della classe. Per controllare l'accesso ai membri di una classe si usano le parole chiave public, protected, private e package (oppure nulla, il default): Specifier private protected public package (o niente) class subclass package X X X X X X X X X X world

Queste parole chiave si mettono davanti al tipo dell'attributo o davanti al tipo di ritorno di un metodo o davanti alla dichiarazione di una classe. Public significa che la classe o il membro visibile a chiunque.

Protected significa che la classe o il membro visibile da classi dello stesso package o sottoclassi nello stesso package. Package significa che la classe o il membro sono visibili solo da classi dello stesso package. Private significa che i membri sono visibili solo dalla classe stessa.

Esempio
Nella classe Libro, la descrizione pu essere molto lunga. Dunque, vale la pena di comprimerla con qualche algoritmo (tipo Zip). Per, non voglio che chi usa la classe libro debba preoccuparsene. Per fare questo nego la visibilit dell'attributo "descrizione", e metto disposizione dei metodi che nascondono i dettagli della compressione
public class Libro { ... // Vettore di bytes che contiene la descrizione compressa private byte[] descrizione ; ... public void setDescrizione(String descrizione) { // algoritmo di compressione che trasforma la stringa in bytes } public String getDescrizione() { // algoritmo di decompressione che restituisce il testo originario }

Getters e setters
Quello precedente il tipico esempio del perch meglio non lasciare mai visibili all'esterno gli attributi di una classe. (Molto) Spesso si nascondono tutti gli attributi di una classe e si creano dei metodi getAttributo() e setAttributo(valore) per leggerne ed impostarne i valori. I metodi di get e set vengono chiamati getters e setters. Come abbiamo visto pu capitare che concettualmente settiamo qualcosa che in realt pu non essere un attributo.