Linguaggi ad oggetti Linguaggi di Programmazione: Paradigmi di Programmazione • I linguaggi di programmazione ad oggetti consentono di applicare metodologie di sviluppo di programmi "object oriented", ossia basate sugli oggetti (dati) che il sistema software manipola. • Questa metodologia si contrappone a quella più tradizionale di sviluppare un programma secondo un approccio funzionale (programmazione strutturata). PROGRAMMAZIONE AD OGGETTI (lucidi originali di Alberto Martelli) Matteo Baldoni Dipartimento di Informatica - Universita` degli Studi di Torino C.so Svizzera, 185 - I-10149 Torino (Italy) e-mail: [email protected] - URL: http://www.di.unito.it/~baldoni Linguaggi di Programmazione: Paradigmi di Programmazione I principali linguaggi ad oggetti Programmazione ad oggetti • In molti casi l'organizzazione ad oggetti risulta la più naturale perché rispecchia in modo diretto il mondo che si vuole rappresentare: • Simula: progettato nel 1967 - capostipite dei linguaggi ad oggetti – simulazione (automobili, semafori, aerei, …) • Smalltalk: linguaggio ad oggetti "puro" linguaggio "dinamico” non c'è controllo statico dei tipi – software grafico (finestre, bottoni, menu, …) • Progetto object oriented di software: costruzione di sistemi software come collezioni strutturate di implementazioni di tipi di dati astratti (B. Meyer) Linguaggi di Programmazione: Paradigmi di Programmazione 3 • C++: linguaggio "ibrido" - garantisce la compatibilità con il C • Java: il più recente. Linguaggi di Programmazione: Paradigmi di Programmazione Java 4 Java • Le caratteristiche principali di Java • Indipendente dall'architettura – il compilatore genera codice intermedio (bytecode) che viene interpretato • Object oriented: – progettato per essere un linguaggio ad oggetti, anche se meno puro di Smalltalk • Applicazioni su internet – applet • Robusto – sicurezza – non ci sono puntatori • Ambiente ricco di tipi di dati predefiniti – garbage collection – grafica • Distribuito – programmazione su internet – costrutti per la concorrenza Linguaggi di Programmazione: Paradigmi di Programmazione 2 – accesso a database 5 Linguaggi di Programmazione: Paradigmi di Programmazione 6 1 Oggetti Incapsulamento Un oggetto rappresenta un dato, ed è costituito da • I dati e le procedure che li manipolano sono raggruppati in un’unica entità, l’oggetto. • stato: collezione di variabili • comportamento: collezione di operazioni (metodi) Esempio - contatore STATO int c; METODI void iniz(int i) void incr() void decr() int val() • Il mondo esterno ha accesso ai dati solo tramite un insieme di operazioni (metodi) che costituiscono l’interfaccia dell’oggetto. I dettagli dell’implementazione sono nascosti (INFORMATION HIDING) inizializza il contatore a i incrementa il contatore di 1 decrementa il contatore di 1 restituisce il valore del contatore Linguaggi di Programmazione: Paradigmi di Programmazione • Un oggetto realizza una ASTRAZIONE DEI DATI Linguaggi di Programmazione: Paradigmi di Programmazione 7 Contatore Invio di Messaggi • Un programma è costituito da un insieme di oggetti. • Gli oggetti sono dinamici - creati e distrutti durante l'esecuzione del programma. • Un oggetto A, per agire su un altro oggetto B, invia un messaggio a B chiedendogli di eseguire uno dei metodi della sua interfaccia. int c; void iniz(int i ) {c=1;} void incr( ) {++c;} void decr( ) {--c;} int val( ) {return c;} void iniz(int i ) void incr( ) void decr( ) int val( ) INTERFACCIA A IMPLEMENTAZIONE CONTATORE Linguaggi di Programmazione: Paradigmi di Programmazione 9 Invio di Messaggi 10 Invio di Messaggi display • invio di messaggio = invocazione di un metodo • In Java, e altri linguaggi, si usa la notazione con il punto: oggetto.metodo(…) contatore invia valore di c 0 c cont1 esegui incr 3 cont2 bottone 3 bottone 1 B esegui il metodo m1 • Lo scambio di messaggi è l'unico modo di comunicare tra oggetti Linguaggi di Programmazione: Paradigmi di Programmazione finestra 8 10 bottone 2 Linguaggi di Programmazione: Paradigmi di Programmazione 11 int x,y; cont1.incr(); cont2.decr(); x = cont1.val(); y = cont2.val(); x ha valore 4 e y ha valore 9 Linguaggi di Programmazione: Paradigmi di Programmazione 12 2 Invio di Messaggi Classi • In un linguaggio tradizionale, per eseguire una operazione su un dato, dovremmo passare il dato come parametro: – incr(cont1) o iniz(cont1, 3) • I più diffusi linguaggi ad oggetti sono basati sul concetto di classe come insieme di oggetti con struttura e comportamento simili • La classe definisce un tipo • Istanza di una classe = oggetto della classe • Nella programmazione ad oggetti, invece, l'oggetto su cui si esegue una operazione non viene passato come parametro, perché figura già come destinatario del messaggio (può essere considerato come un parametro implicito) – cont1.incr() o cont1.iniz(3) Linguaggi di Programmazione: Paradigmi di Programmazione 13 Classi in Java Classe Contatore • In Java: class Contatore { int c; void iniz(int i) {c = i;} void incr() {++c;} void decr() {--c;} int val() {return c;} } 10 Linguaggi di Programmazione: Paradigmi di Programmazione 14 • Per realizzare information hiding si usano le parole riservate – public - interfaccia – private - implementazione • Per il momento trascuriamo questo aspetto class Contatore { private int c; public void iniz(int i) {c = i;} public void incr() {++c;} public void decr() {--c;} public int val() {return c;} } 15 Linguaggi di Programmazione: Paradigmi di Programmazione Variabili private 16 Tipi class Contatore { private int c; public void iniz(int i) {c = i;} public void incr() {++c;} public void decr() {--c;} public int val() {return c;} } • Si noti che, se la variabile c fosse public, il metodo val sarebbe inutile: per conoscere il valore di un contatore cont basterebbe usare cont.c • Tuttavia è buna norma di programmazione impedire l’accesso diretto alle variabili di un oggetto, dichiarandole private. Linguaggi di Programmazione: Paradigmi di Programmazione cont2 3 Classi in Java • Una classe realizza l'implementazione di un tipo di dato astratto. Linguaggi di Programmazione: Paradigmi di Programmazione Istanze cont1 17 • Java è un linguaggio tipato: tutte le variabili hanno un tipo. • Il tipo di una variabile deve sempre essere dichiarato e può essere: – una classe – un tipo primitivo Contatore cont; int x,y; char ch; Linguaggi di Programmazione: Paradigmi di Programmazione 18 3 Tipi di dato primitivi Come si crea un’istanza • Java fornisce diversi tipi semplici primitivi. • A differenza di altri linguaggi, la dimensione dei tipi numerici è fissata per consentire la portabilità dei programmi: – – – – – – – – byte short int long float double char boolean 8 16 32 64 32 64 16 bit bit bit bit bit bit bit (Unicode) Linguaggi di Programmazione: Paradigmi di Programmazione • new Contatore() • Crea un nuovo oggetto di tipo Contatore e ne restituisce il puntatore (handle) • Tutti gli oggetti sono allocati dinamicamente (quando si esegue la new) e sono manipolati attraverso una handle (puntatore) (assegnamento, passaggio di parametri) 19 Gestione della memoria Linguaggi di Programmazione: Paradigmi di Programmazione 20 Gestione della memoria STATICA dati globali • Nella maggior parte dei linguaggi di programmazione le attivazioni di procedure sono gestite con uno stack (pila) • Quando una procedura è chiamata, si inserisce un record di attivazione in cima alla pila, e lo si toglie quando l'esecuzione della procedura termina Codice STACK record di attivaz. dati locali • Un record di attivazione contiene le informazioni sul controllo (indirizzo di ritorno) ed i dati locali HEAP dati dinamici • Possibilità di chiamate ricorsive di procedure Linguaggi di Programmazione: Paradigmi di Programmazione 21 Recupero della memoria Linguaggi di Programmazione: Paradigmi di Programmazione 22 Allocazione dinamica Contatore cont1, cont2; • Garbage collection (Scheme, Prolog, Java) cont1 cont2 • Il programmatore può solo allocare dati dinamici • Una procedura di sistema, il garbage collector, si preoccupa di recuperare tutte le aree di memoria nello heap non più raggiungibili in modo da poterle riutilizzare • Altri linguaggi come Pascal o C lasciano al programmatore la responsabilità di recuperare la memoria, con possibilità di commettere errori Linguaggi di Programmazione: Paradigmi di Programmazione 23 Linguaggi di Programmazione: Paradigmi di Programmazione 24 4 Allocazione dinamica Contatore cont1, cont2; cont1 = new Contatore(); cont2 = new Contatore(); cont1.iniz(3); cont2.iniz(10); Allocazione dinamica Contatore cont1, cont2; cont1 = new Contatore(); cont2 = new Contatore(); cont1.iniz(3); cont2.iniz(10); cont2 = cont1; cont1 cont2 3 10 cont1 cont2 3 10 Questo oggetto non è più accessibile. Può essere recuperato dal garbage collector. HEAP Linguaggi di Programmazione: Paradigmi di Programmazione HEAP 25 Puntatori 26 Come si inizializza un oggetto • In Java non esistono puntatori espliciti • Si può chiamare esplicitamente un metodo di inizializzazione: Contatore cont1; cont1 = new Contatore(); cont1.iniz(3); Contatore cont; • Fino a quando non si esegue una new, la variabile cont non è associata a nessun oggetto • Viceversa in C++ si può dichiarare sia Contatore cont1; sia Contatore *cont2; • cont1 è associato ad un contatore creato "staticamente”, cont2 è associato ad un contatore creato dinamicamente (vedi Java) Linguaggi di Programmazione: Paradigmi di Programmazione 27 Costruttori class Contatore { int c; Contatore(int i) {c = i;} void incr() {++c;} void decr() {--c;} int val() {return c;} } Linguaggi di Programmazione: Paradigmi di Programmazione • L'inizializzazione di un oggetto è una operazione molto importante. • Java (e C++) forniscono la nozione di costruttore, che consente di inizializzare automaticamente un oggetto al momento della creazione. Linguaggi di Programmazione: Paradigmi di Programmazione 28 Overloading dei costruttori costruttore • L'oggetto viene inizializzato al momento della creazione, invocando automaticamente il costruttore. • L’istruzione Contatore cont1 = new Contatore(3); crea un contatore e lo inizializza a 3. Linguaggi di Programmazione: Paradigmi di Programmazione 29 class Contatore { int c; Contatore() {c = 0;} Contatore(int i) {c = i;} void incr() {++c;} void decr() {--c;} int val() {return c;} } Ci sono 2 costruttori diversi, individuati dal numero e tipo degli argomenti. Contatore cont1 = new Contatore(); Contatore cont2 = new Contatore(5); //iniz. a 0 //iniz. a 5 Linguaggi di Programmazione: Paradigmi di Programmazione 30 5 Overloading di metodi THIS • Overloading: metodi diversi possono avere lo stesso nome. • I metodi "overloaded" si distinguono uno dall'altro in base alla lista dei tipi degli argomenti. • Come può un oggetto mandare un messaggio a se stesso, ossia invocare un proprio metodo? • Con la parola chiave this class Contatore { int c; Contatore() {c = 0;} Contatore(int i) {c = i;} void incr() {++c;} void incr(int n) {c += n;} void decr() {--c;} int val() {return c;} } class A { ... void p() {...} void m() { ... this.p(); ...} } In alcuni casi this è indispensabile si può anche scrivere solo p() this viene aggiunto dal compilatore Linguaggi di Programmazione: Paradigmi di Programmazione 31 Linguaggi di Programmazione: Paradigmi di Programmazione 32 I tipi semplici non sono oggetti THIS come costruttore Contatore cont; cont = new Contatore(3); int x = 10; • this da solo indica un costruttore della stessa classe cont x 3 class Contatore { int c; Contatore(int n) {c=n;} Contatore() {this(10);} void incr() {++c;} void decr() {--c;} int val() {return c;} } chiama il costruttore con un argomento (inizializza il contatore a 10) Linguaggi di Programmazione: Paradigmi di Programmazione 33 Array in Java • La dimensione è fissata al momento della creazione oppure oppure • Esistono classi wrapper che trasformano tipi semplici in oggetti HEAP Linguaggi di Programmazione: Paradigmi di Programmazione 34 Array in Java • Gli array in Java sono oggetti che vengono allocati dinamicamente Contatore cont[]; int num_giorni[]; • I tipi semplici, come ad es. gli interi, non vengono allocati dinamicamente nello heap, a differenza di altri linguaggi "puri" come Smalltalk Contatore[] cont; int[] num_giorni; • L'interprete controlla che gli indici siano sempre nell'intervallo specificato al momento della creazione • num_giorni[13] darà errore a "runtime" cont = new Contatore[3]; num_giorni = new int[12]; Linguaggi di Programmazione: Paradigmi di Programmazione 35 Linguaggi di Programmazione: Paradigmi di Programmazione 36 6 Array Array Contatore cont[]; Contatore cont[]; cont = new Contatore[3]; cont cont Linguaggi di Programmazione: Paradigmi di Programmazione Linguaggi di Programmazione: Paradigmi di Programmazione 37 Array Stringhe Contatore cont[]; cont = new Contatore[3]; cont[0] = new Contatore(10); • La libreria standard fornisce una classe predefinita String String s1 = "Buon"; String s2 = "giorno"; String saluto = s1 + s2; contatore cont 38 La stringa saluto ha valore: “Buongiorno” Il + indica concatenazione 10 • La classe String contiene numerosi metodi: String s = saluto.substring(0,4) //s ha valore "Buon" int n = saluto.length() Linguaggi di Programmazione: Paradigmi di Programmazione Linguaggi di Programmazione: Paradigmi di Programmazione 39 Stringhe 40 Uguaglianza di stringhe • Le stringhe sono immutabili restituisce true se le due stringhe s e t sono uguali, false altrimenti s.equals(t) • Non ci sono metodi per cambiare un carattere in una stringa: occorre creare una nuova stringa String s = "salve"; String t = "salve"; dà true s.equals(t) String s = "salve"; se vogliamo sostituire la e con una a String s = s.substring(0,4) + "a"; s == t oppure String s = "salva"; Linguaggi di Programmazione: Paradigmi di Programmazione // n vale 10 41 non va bene usare l'operatore == darebbe false perché i valori di s e t sono due oggetti diversi, anche se hanno lo stesso valore Linguaggi di Programmazione: Paradigmi di Programmazione 42 7 Variabili e metodi di classe Variabili e metodi di classe • E' possibile definire variabili (o metodi) associate ad una classe, condivise da tutte le istanze di quella classe • In Java sono individuate dalla parola chiave static cont2 numContatori c 8 • Le variabili di classe sono visibili da tutte le istanze della classe. • Sono variabili globali (per tutte le istanze di una classe). Linguaggi di Programmazione: Paradigmi di Programmazione 43 Variabili e metodi di classe Linguaggi di Programmazione: Paradigmi di Programmazione 44 Programmi in Java • Una classe può essere considerata come un oggetto (di tipo Class) che viene allocato staticamente (all'inizio dell'esecuzione del programma) • Le variabili ed i metodi di classe (static) sono accessibili attraverso il nome della classe int x,y; Conta cont = new Conta(3); cont.incr(); .... x = cont.c; y = Conta.numContatori; • Un programma in Java è una collezione di classi • Una di queste deve contenere un metodo main • L'esecuzione inizia dal main • Il metodo main deve essere statico, perché altrimenti bisognerebbe creare un oggetto della classe Esempio prima di poterlo eseguire class Esempio { public static void main(String arg[]) { System.out.println("questo è un esempio"); } } Linguaggi di Programmazione: Paradigmi di Programmazione 45 Esecuzione di programmi: Interprete Linguaggi di Programmazione: Paradigmi di Programmazione 46 Esecuzione di programmi: Compilatore (traduttore) LS = ling. sorgente LM = ling. macchina LS c 5 2 class Conta { static int numContatori = 0; int c; Conta(int i) {c = i; ++numContatori;} void incr() {++c;} void decr() {--c;} int val() {return c;} } Progr. sorgente cont1 classe Conta Interprete Progr. sorgente Compilatore Progr. oggetto LS LM1 LM2 LM L'esecuzione mediante compilatore avviene in due fasi: l'interprete esegue direttamente le operazione del progr. sorgente • traduzione da linguaggio sorgente a linguaggio macchina • esecuzione del programma in linguaggio macchina Linguaggi di Programmazione: Paradigmi di Programmazione 47 Linguaggi di Programmazione: Paradigmi di Programmazione 48 8 Esecuzione di programmi: Soluzione Mista Progr. sorgente Compilatore Programma in ling. intermedio Interprete JAVA LM1 bytecode LM2 Vantaggi • Compilazione (Pascal, C, Ada, C++): Efficienza di esecuzione. Il codice generato dal compilatore può essere ottimizzato, perché la compilazione è fatta una sola volta • Interprete (JavaScript, Scheme, Prolog): Interazione. Più facile modificare un programma durante l'esecuzione • Adottata da Java. • Soluzione mista (Java): Portabilità. Per eseguire un programma su macchine diverse è sufficiente implementare l'interprete del linguaggio intermedio, e non tutto il compilatore • Il linguaggio intermedio si chiama bytecode. Linguaggi di Programmazione: Paradigmi di Programmazione 49 Compilazione di un programma in Java Linguaggi di Programmazione: Paradigmi di Programmazione 50 Esecuzione di un programma in Java • Si crea un file <nome_file>.java contenente una o più classi C1, C2, … e lo si compila. • Si chiama l'interprete su una classe che contiene il main (Es. C1) > java C1 > javac <nome_file>.java • L'interprete alloca questa classe e comincia ad eseguire il main. • Il compilatore crea un file .class in codice intermedio (bytecode) per ogni classe contenuta nel file .java: C1.class, C2.class, ... Linguaggi di Programmazione: Paradigmi di Programmazione 51 Un programma completo class Contatore { int c; Contatore(int n) {c=n;} void incr() {++c;} void decr() {--c;} int val() {return c;} } Linguaggi di Programmazione: Paradigmi di Programmazione 52 Compilazione in Java • Una unità di compilazione è un file .java che contiene delle definizioni di classi. > javac <nome_file>.java > java UsaCont • Il compilatore produce un file .class per ogni classe nel file. class UsaCont { public static void main(String arg[]) { Contatore cont = new Contatore(5); cont.incr(); System.out.println("valore =" + cont.val()); } } Linguaggi di Programmazione: Paradigmi di Programmazione • Al massimo una di queste classi può essere public: in questo caso deve avere lo stesso nome del file (senza .java). • L'interprete ha la responsabilità di caricare e interpretare questi file. 53 Linguaggi di Programmazione: Paradigmi di Programmazione 54 9