In un linguaggio imperativo tradizionale (non O.O.) ci sono:
Il tipo "rettangolo" e' un record con due interi per lunghezza e larghezza. La funzione "area" prende un rettangolo e ne ritorna l'area. La funzione "print" stampa un rettangolo.
#include <stdio.h> typedef struct { int length, width; } Rectangle; int area(Rectangle r) { return r.length * r.width; } void print(Rectangle r) { printf("Rettangolo %d x %d\n",r.length,r.width); } void main() { Rectangle r; int a; r.length = 3; r.width = 5; a = area(r); print(r); printf("Area = %d\n",a); }
In un linguaggio object oriented:
La classe "rettangolo" ha due interi per lunghezza e larghezza, un metodo "area" che ritorna l'area del rettangolo e un metodo "print" che stampa il rettangolo.
class Rectangle { int length, width; int area() { return length * width; } void print() { System.out.println("Rectangle " + length + " x " + width); } public static void main(String[] argc) { Rectangle r = new Rectangle(); int a; r.length = 3; r.width = 5; a = r.area(); r.print(); System.out.println("Area = " + a); } }
Convenzione: in Java i nomi di classe hanno iniziale maiuscola, gli altri nomi iniziale minuscola. Se un nome e' fatto da piu' parole, le successive hanno iniziale maiuscola (es. "printArea").
Notazione analoga alla selezione di un campo di un record. Es. se r e' un rettangolo:
L'oggetto su cui si chiama il metodo e' argomento privilegiato del metodo.
All'interno dell'implementazione del metodo l'argomento privilegiato
e' lasciato implicito,
ma posso anche esplicitarlo usando la parola chiave "this".
Es. nel rettangolo e' equivalente scrivere:
int area() { return length * width; } int area() { return this.length * this.width; }
Usare "this" e' indispensabile quando si vuole ritornare questo oggetto come risultato. Esempio:
Rectangle bigger(Rectangle r) { if (this.area() > r.area()) return this; else return r; }
Una classe e' l'analogo di un tipo ma comprende anche le funzioni
che agiscono su quel tipo.
E' come un record dove alcuni campi sono variabili ed altri sono funzioni.
Analogia tra classe e modulo:
Come si dice "un valore di un tipo" cosi' si dice "un oggetto di una
classe".
Si dice che un oggetto e' istanza della sua classe.
Un oggetto ha:
Un oggetto e' la "nobilitazione" di un puntatore. L'identita' di un oggetto e' la locazione di memoria dove stanno scritti i suoi dati. I valori contenuti in questa locazione possono cambiare nel tempo ma la locazione e' sempre la stessa.
Classe = Modulo come scatola chiusa. Che cosa si vede da fuori la scatola?
Attributi e metodi possono essere dichiarati come pubblici o privati.
In java devo ripetere le parole chiave "public" e "private" prima di ogni variabile o funzione. Dove non specificato, si intende "public".
Si dice che una classe incapsula lo stato dei suoi oggetti quando tutte le variabili che descrivono lo stato sono private, percio' da fuori si possono leggere ed assegnare solo mediante le funzioni definite nella classe.
Buona norma: definire attributi privati con metodi "set" e "get" pubblici. Es. per il rettangolo:
class Rectangle { private int length, width; public void setLength (int l) { length = l; } public void setWidth (int w) { width = w; } public int getLength() { return length; } public int getWidth() { return width; } ... }allora non posso piu' scrivere
r.length = 3; r.width = 5;ma devo scrivere invece
r.setLength(3); r.setWidth(5);
Incapsulazione e' particolarmente utile
quando i valori dello stato sono soggetti a
vincoli di validita', e quindi e' importante evitare
che qualcuno li modifichi in modo incontrollato.
Esempio: dimensioni del rettangolo non possono essere negative:
public void setLength (int l) { if (l>=0) length = l; else System.out.println("Negative length!"); } public void setWidth (int w) { if (w>=0) width = w; else System.out.println("Negative width!"); }
Metodi privati tipicamente sono funzioni ausiliarie usate solo dentro la classe per facilitare l'implementazione di altri metodi.
Metodo speciale che ogni classe ha, serve per fabbricare nuove istanze. Serve per allocare e inizializzare lo stato di un oggetto al momento della sua creazione. Puo' avere parametri a piacere ma non ha valore di ritorno.
Se non lo scrivo nella dichiarazione della classe, il sistema mette automaticamente un costruttore di default: senza argomenti, crea istanza mettendo tutte le variabili a zero. Si chiama con questa sintassi:
Rectangle r = new Rectangle();
Posso scrivere io un costruttore, anche con parametri. Es. per il rettangolo:
Rectangle(int l, int w) { length = l; width = w; }
Si chiama con questa sintassi:
Rectangle r = new Rectangle(5,12);
Se scrivo il mio costruttore, perdo quello di default. Se lo voglio, devo ridefinirlo. Es. nel caso del rettangolo:
Rectangle() { length = width = 0; }
Nota: una classe puo' avere piu' costruttori con parametri diversi. E' un caso di polimorfismo.
In linguaggio tradizionale non posso avere funzioni diverse
(cioe' con diversi tipi per argomenti e/o risultato) e con nome uguale.
In linguaggio object oriented si puo'. Si chiama "polimorfismo".
Esempio: nella classe rettangolo ora abbiamo due costruttori.
Polimorfismo vale non solo per i costruttori. Es. nel rettangolo aggiungo funzione "assegna dimensioni" che ha due forme:
void setSize(int l, int w) { length = l; width = w; } void setSize() { length = width = 0; }
Naturalmente posso avere funzioni con stesso nome in classi diverse. E questo non e' polimorfismo (e' come avere campi con stesso nome in tipi record diversi).
Variabili di istanza
Ne esistera' una per ciascun oggetto della classe.
Valore lo decide ciascun oggetto per la sua.
Valori di solito diversi in oggetti diversi
(come tradizionalmente i campi di un record).
Si accedono con notazione obj.var, dove obj e' un oggetto
e var e' il nome della variabile.
Variabili di classe
Ne esiste una sola condivisa da tutti gli oggetti della classe.
Valore lo decide la classe, o un oggetto qualsiasi della classe.
Il cambiamento viene visto anche da tutti gli altri oggetti
della classe.
Si accedono con notazione Cls.var dove Cls e' il nome della classe
oppure con obj.var, dove obj e' un oggetto qualsiasi della classe
(ma molto meglio il primo modo)
Funzioni di istanza
Agiscono su un oggetto che funge da argomento implicito.
Si chiamano con la notazione obj.fun(...) dove obj e' un oggetto della
classe, obj sara' l'argomento implicito.
Quando, dentro il body di fun, appare una variabile, questa
variabile e' quella dell'oggetto obj.
Funzioni di classe
Non hanno argomenti impliciti.
Sono un po' come le funzioni tradizionali dei linguaggi imperativi,
ma sono legate ad una classe.
Si chiamano con la notazione Cls.fun(...) dove Cls e' il nome della classe
oppure con obj.fun(...), dove obj e' un oggetto qualsiasi della classe
(ma molto meglio il primo modo)
In Java variabili e funzioni di classe sono contrassegnate da parola chiave "static".
Esempio: nella classe "rettangolo" posso mettere variabile di classe
static int minimumDimension = 0;e usarla nelle funzioni che assegnano lunghezza e larghezza:
void setLength(int l) { if (l>=minimumDimension) length = l; else System.out.println("Invalid length!"); }se a un certo punto decido di accettare solo rettangoli con lati lunghi almeno 10, basta mettere minimumDimension = 10.
Posso renderla una costante (cioe' il suo valore non potra' piu' essere cambiato) premettendo la parola chiave "final":
static final int minimumDimension = 0;
Convenzione: i nomi di costanti sono a lettere tutte maiuscole (come le macro in C). Quindi meglio scrivere:
static final int MINIMUM_DIMENSION = 0;
Dalla sua costruzione, un oggetto ha sua propria identita' = locazione di memoria dove viene memorizzato.
Linguaggi o.o. tengono una tabella degli oggetti esistenti. L'indice nella tabella e' l'identita' dell'oggetto (OID = object identifier).
Identita' resta inalterata in seguito a modifiche dello stato dell'oggetto
(= valori dei suoi attributi).
Modifiche cambiano contenuto della memoria ma non sua locazione.
Esempio:
1. Rectangle r1 = new Rectangle(2,3); 2. Rectangle r2 = new Rectangle(5,3); 3. r1.setLength(5); 4. Rectangle r3 = r1; 5. r3.setWidth(4); 6. r1 = new Rectangle(1,8); 7. r3 = new Rectangle(10,15);
Un oggetto non e' la variabile che lo denota.
Piu' variabili possono "puntare" lo stesso oggetto (r1 ed r3 dopo istruzione 4).
Un oggetto esiste indipendentemente da quali variabili lo puntanto (dopo istruzione 6 l'oggetto nato come r1 e' puntato solo da r3).
Java, quando un oggetto non e' piu' puntato da nessuna variabile,
lo cancella: libera la sua locazione di memoria e la rende disponibile
per altri oggetti, cancella il suo OID (es. dopo istruzione 7).
Questo meccanismo si chiama garbage collection
(= raccolta della spazzatura).
In molti linguaggi O.O. incluso Java c'e' garbage collection = gestione automatica di allocazione e deallocazione della memoria dinamica.
La memoria di un oggetto viene allocata alla sua creazione (chiamata al costruttore della classe con istruzione "new" ) e viene deallocata automaticamente quando non e' piu' accessibile attraverso nessuna variabile.
Tra oggetti e' definito l'operatore booleano ==.
Tale operatore confronta gli OID, cioe' le identita' dei due oggetti (le locazioni di memoria).
Risulta vero se hanno la stessa identita' = stessa locazione di memoria. Es. dopo istruzione 4, r1==r3 vale vero.
Se due oggetti hanno stato uguale (stesso contenuto della locazione di memoria) ma non stessa identita' (non stessa locazione) risulta falso. Es. dopo istruzione 3, r1==r2 vale falso.
Se voglio funzione che controlli quando due oggetti diversi hanno uguale stato, cioe' che confronti il contenuto delle locazioni di memoria, devo definirla.
Esempio:
boolean isEqual(Rectangle r) { return ( (r.length==length) && (r.width==width) ); }Si chiama con r1.isEqual(r2); Uno dei due rettangoli e' argomento privilegiato (asimmetria).
Alternativa con metodo di classe:
static boolean isEqual(Rectangle r1, Rectangle r2) { return ( (r1.length==r2.length) && (r1.width==r2.width) ); }Simmetria di trattamento fra i due argomenti. Si chiama con Rectangle.isEqual(r1,r2); Oppure con r.isEqual(r1,r2); dove r e' qualsiasi istanza della classe Rectangle (ma quest'ultimo modo e' sconsigliato).
Essendo gli attributi length e width di tipi base (tipo int), le locazioni di memoria di due rettangoli contengono valori base. Percio' l'operatore == nel corpo di isEqual non pone problemi.
Se invece gli attributi fossero oggetti di una classe, si porrebbe il problema se la loro uguaglianza vada controllata con == direttamente oppure di nuovo con isEqual. Otteniamo due diverse nozioni di uguaglianza:
class RectanglePair { Rectangle first; Rectangle second; ... boolean shallowEqual(RectanglePair rp) { return ( (first==rp.first) && (second==rp.second) ); } boolean deepEqual(RectanglePair rp) { return ( first.isEqual(rp.first) && second.isEqual(rp.second) ); } }Shallow: risulta vero se rp e' coppia con esattamente gli stessi rettangoli che ha questa coppia.
Discorso analogo a quello sulle varie nozioni di uguaglianza. Varie nozioni di copia.
Assegnazione r3=r1 fa puntare la variabile r3 allo stesso oggetto puntato da r1. Dopo, r1==r3 vale vero.
Se voglio creare un altro oggetto uguale a r1, devo scrivere una funzione di copia. Esempio:
Rectangle copy() { return new Rectangle(length,width); }Poi posso chiamare r3 = r1.copy(); Dopo, r1==r3 vale falso, ma Rectangle.isEqual(r1,r3) vale vero.
Di solito si scrive un costruttore di copia:
Rectangle(Rectangle r) { length = r.length; width = r.width; }Poi posso chiamare r3 = new Rectangle(r1); Costruisce un oggetto copia.
NOTA: Se gli attributi sono a loro volta oggetti, la copia puo' essere shallow o deep.
Java e' linguaggio totalmente object oriented. A parte i tipi base (int, float, double, boolean, char) tutti gli altri tipi sono classi.
Anche il "main" e' una funzione in una classe, che deve avere i seguenti parametri:
public static void main(String[] args)Posso "eseguire" qualsiasi classe contenga questa funzione. Quello che viene eseguito e' il main.
Gli argomenti della funzione "main" sono i parametri dati su
command-line quando si lancia il programma:
args[0] = il primo parametro
args[1] = il secondo parametro
ecc.
String e' classe predefinita Java per le stringhe di caratteri.
Esempio: sulla command-line accetto una stringa, un intero e un float. Li "decodifico" in questo modo:
class SampleMain { public static void main(String[] args) { String primo; int secondo; float terzo; if (args.length!=3) System.out.println("il programma va lanciato con tre argomenti"); else { primo = args[0]; System.out.println("Primo argomento stringa = " + primo); secondo = Integer.parseInt(args[1]); System.out.println("Secondo argomento intero = " + secondo); terzo = Float.parseFloat(args[2]); System.out.println("Terzo argomento float = " + terzo); } } }
I sorgenti java possono essere scritti con qualsiasi editor di testi. Importante, se si usa un word processor (come MS-Word) salvare in formato "solo testo", senza formattazione.
La classe XXX va messa in un file chiamato XXX.java (stesso nome
della classe con estensione .java).
Attenzione: quando si salva in formato "solo testo", l'editor
potrebbe dare automaticamente estensione .txt: bisogna ricordarsi di
cambiarla.
Un programma puo' consistere di piu' file (uno per classe). Java rintraccia automaticamente i file che servono, purche' siano nella directory corrente.
Java e' linguaggio pensato per generare programmi portabili su piattaforme ddiverse senza essere ricompilati ("compile once, execute everywhere").
Posso eseguire su qualunque macchina sia installato l'interprete anche se non ha il compilatore. In rete (http://java.sun.com/) sono disponibili per solaris / windows / linux:
I sorgenti devono essere prima compilati e poi eseguiti.
La versione di Java installata in laboratorio e' la 1.4.
path %path%;c:\j2sdk1.4.2_05\bin;Per evitare di dover digitare questo comando a mano, potete scaricare il file batch mypath.bat ed eseguirlo digitandone semplicemente il nome (il file contiene il comando ed eseguire il file equivale a digitare il comando contenuto):
mypath
Come dare gli argomenti da command-line, esempio:
java SampleMain pippo 12 13.8produce:
Primo argomento stringa = pippo Secondo argomento intero = 12 Terzo argomento float = 13.8
Java ha una grande libreria di classi predefinite (Java Foundation Classes, JFC), tra cui:
Manuale dei package e delle classi predefinite Java disponibile in rete (http://java.sun.com/reference/api/index.html).