Programmazione Object Oriented PDF
Document Details
Uploaded by Deleted User
Tags
Summary
This document provides an introduction to object-oriented programming concepts. It discusses different programming languages, focusing on high-level languages and object-oriented paradigms. It explains how these languages allow for more abstract problem-solving, emphasizing modularity and reusability.
Full Transcript
Concetti introduttivi La programmazione Programma: sequenza di operazioni semplici (istruzioni e decisioni) eseguite in successione. ⚫ Un programma indica al computer i passaggi da compiere per svolgere un compito preciso. ⚫ I programmi danno flessibilità di impieg...
Concetti introduttivi La programmazione Programma: sequenza di operazioni semplici (istruzioni e decisioni) eseguite in successione. ⚫ Un programma indica al computer i passaggi da compiere per svolgere un compito preciso. ⚫ I programmi danno flessibilità di impiego ai computer L’attività di progettazione e implementazione dei programmi è detta programmazione. I programmi sono scritti utilizzando linguaggi di programmazione Linguaggi di Programmazione I linguaggi di programmazione sono in genere classificati in Linguaggi macchina ⚫ Istruzioni macchina codificate 30 40 16 100 con sequenze numeriche 156 ⚫ Dipendenti dalla macchina Linguaggi assembly LOAD REG, loc_b ⚫ Istruzioni macchina codificate ADD REG, loc_a con codici mnemonici MOV loc_b, REG ⚫ Dipendenti dalla macchina Linguaggi di alto livello (C, Pascal, Java, ecc.) ⚫ Istruzioni ad un livello concettuale più elevato b = a+b; ⚫ Indipendenti dalla macchina Linguaggi di alto livello I linguaggi di alto livello consentono un maggiore livello di astrazione ⚫ Permettono di descrivere l’idea che sta dietro l’operazione da compiere Esempio: if x>0 then print(“x è positivo”) else print(“x è negativo”) Sono più vicini ai linguaggi naturali Seguono delle convenzioni rigide per facilitarne la traduzione in codice macchina (compilazione) Compilazione Le istruzioni scritte in un linguaggio ad alto livello devono essere tradotte in istruzioni macchina per poter essere “comprese” dalla CPU ⚫ Il compilatore è il programma che si occupa di tradurre il codice L’insieme di istruzioni macchina (linguaggio macchina) dipende dalla CPU ⚫ Il “back-end” di un compilatore dipende dalla CPU Linguaggi di alto livello I linguaggi di alto livello possono essere classificati in vari modi. Di interesse per il corso: ⚫ Linguaggi procedurali o imperativi C, Pascal, … ⚫ Linguaggi orientati agli oggetti C++, Java, … Altre classi di linguaggi: ⚫ Linguaggi funzionali Lisp, SML, … ⚫ Linguaggi logici o dichiarativi Prolog, LDL, … Limite dei linguaggi procedurali Costringe a pensare soluzioni che riflettono il modo di operare del computer piuttosto che la struttura stessa del problema. ⚫ Per problemi non numerici questo spesso è difficile ⚫ Il riutilizzo delle soluzioni è più complicato e improbabile ⚫ La produzione e la manutenzione del software sono costose Esempio Scrivere un programma per la gestione di un conto corrente bancario Dove sono finiti i concetti di conto corrente, prelievo, versamento, saldo corrente ? Linguaggi Orientati agli Oggetti I linguaggi ad oggetti permettono al programmatore di rappresentare e manipolare non solo dati numerici o stringhe ma anche dati più complessi e aderenti alla realtà (conti bancari, schede personali,…) ⚫ Progettazione e sviluppo più semplice e veloce ⚫ Alta modularità Estensibilità e manutenzione più semplici Tutto questo si traduce in costi più bassi Esistono controindicazioni? Il paradigma di programmazione orientata agli oggetti paga la sua semplicità e versatilità in termini di efficienza Va molto bene per lo sviluppo di applicazioni, ma non è adatto per lo sviluppo di software di base ⚫ Sistemi operativi ⚫ Driver ⚫ Compilatori Dominio del problema e dominio della soluzione I linguaggi procedurali definiscono un “dominio della soluzione” che “astrae” la macchina sottostante ⚫ Astrazione procedurale Il programmatore deve creare un mapping fra “dominio del problema” e “dominio della soluzione” ⚫ Tale mapping è spesso innaturale e di difficile comprensione Linguaggi orientati agli oggetti Forniscono astrazioni che consentono di rappresentare direttamente nel dominio della soluzione gli elementi del dominio del problema ⚫ Oggetti ⚫ Classi ⚫ Messaggi Astrazione Astrazione: Una vista di un oggetto che si focalizza sulle informazioni rilevanti ad un particolare scopo e che ignora le informazioni rimanenti Information Hiding: Un tecnica per lo sviluppo del software in cui le interfacce dei moduli mostrano il meno possibile del loro funzionamento interno e gli altri moduli sono prevenute dall’usare informazioni del modulo che non sono definite nell’interfaccia Modelli I modelli sono nati prima dei calcolatori e NON devono necessariamente essere realizzati mediante calcolatori Chiamate Operatori Elementi del modello Ogni modello è formato da elementi che rappresentano entità Gli elementi del modello presentano un comportamento consistente A seconda dei loro comportamenti comuni, gli elementi possono essere raggruppati in categorie diverse Il comportamento di un elemento può essere provocato da azioni esterne Modelli in Java Elementi del modello: Oggetti La categorie di oggetti vengono chiamate Classi Una classe ⚫ Determina il comportamento degli oggetti appartenenti ⚫ E’ definita da una sezione di codice Un oggetto ⚫ Appartiene ad una classe ⚫ Costituisce una istanza di tale classe Esempio Classe operatore: ⚫ Definisce il comportamento degli operatori (ad esempio, cambiamento di locazione, registrano il tempo di un intervento, ecc.) ⚫ Ogni operatore in servizio è una istanza di tale classe Classe chiamata: ⚫ Definisce il comportamento delle chiamate (ad esempio, priorità, orario di arrivo, cliente chiamante ecc.) ⚫ Per ogni chiamata che arriva si crea una istanza Programmazione OO Focus: gli oggetti ⚫ e le classi che ne definiscono il comportamento Filosofia: In un programma in esecuzione sono gli oggetti che eseguono le operazioni desiderate Programmare in Java ⚫ Scrivere le definizioni delle classi che modellano il problema ⚫ Usare tali classi per creare oggetti Java è dotato di classi ed oggetti predefiniti ⚫ Non si deve continuamente reinventare la ruota Obiettivi Formalizzare oggetti del mondo reale in oggetti del dominio applicativo che possano essere utilizzati dall’applicazione, individuando solo gli aspetti che interessano e tralasciando tutto ciò che è superfluo Riuscire a fare interagire gli oggetti tra loro, al fine di raggiungere l’obiettivo per il quale è stata creata l’applicazione Implementare codice in modo tale che sia il più possibile conforme agli standard di programmazione Distinguere gli oggetti Esempio: un Bicchiere Ne sappiamo definire le caratteristiche e conosciamo anche quali azioni si possono fare con esso. Possiamo definirne la forma, il colore, il materiale di cui è fatto e possiamo dire se è pieno o vuoto. Sappiamo anche che si può riempire e svuotare. Abbiamo definito un oggetto attraverso ⚫ le sue caratteristiche ⚫ le operazioni che può compiere Il livello di astrazione I linguaggi di programmazione si sono evoluti in modo che i codici sorgenti potessero astrarsi sempre più dal modo in cui gli stessi, una volta compilati, sarebbero stati eseguiti. Nella OOP non ci si vuole più porre i problemi dal punto di vista del calcolatore, ma si vogliono risolvere facendo interagire oggetti del dominio applicativo come fossero oggetti del mondo reale. L’obiettivo è di dare uno strumento al programmatore, per formalizzare soluzioni ai propri problemi, pensando come una persona e senza doversi sforzare a pensare come una macchina. Il processo di astrazione: le classi Per popolare il dominio applicativo utilizzato dall’applicazione è necessario creare gli oggetti, e per fare questo è necessario definire le classi. Una classe è lo strumento con cui si identifica e si crea un oggetto. Una classe è un modello per la creazione di oggetti La classe è paragonabile allo stampo gli oggetti sono i biscotti ottenuti con quello stampo Classi e tipi di dato Una classe è a tutti gli effetti un tipo di dato (come gli interi e le stringhe e ogni altro tipo già definito) Nella programmazione orientata agli oggetti, è quindi possibile sia utilizzare tipi di dato esistenti, sia definirne di nuovi tramite le classi Oggetti, astrazione ed information hiding Oggetti, astrazione ed information hiding Nome Cliente Numero CC Disponibilità Versa Preleva Saldo Corrente Messaggi Gli oggetti sono gli elementi attivi di un programma. Come fanno gli oggetti a compiere le azioni desiderate ? Gli oggetti sono attivati dalla ricezioni di un messaggio Una classe determina i messaggi a cui un oggetto può rispondere I messaggi sono inviati da altri oggetti Messaggi Messaggi Per l’invio di un messaggio è necessario specificare: ⚫ Ricevente ⚫ Messaggio ⚫ Eventuali informazioni aggiuntive Non tutti i messaggi sono comprensibili da un determinato oggetto: ⚫ Es: abbassare il volume ⚫ Un messaggio deve invocare un comportamento dell’oggetto Nomi e referenze Le classi hanno un nome ⚫ Ogni classe Java deve avere un nome ⚫ Ogni classe ha un solo nome ⚫ Es: Impiegato, Molecola, ContoCorrente ⚫ Convenzione: comincia con una lettera maiuscola Regole Java per i nomi (identificatori) ⚫ Lettere, cifre e caratteri speciali (es: “_”) ⚫ Devono cominciare con una lettera ⚫ Il linguaggio è case sensitive Nomi e referenze Gli oggetti NON hanno nome ⚫ In Java gli oggetti sono identificati da riferimenti ⚫ Un riferimento (reference) è una frase che si riferisce ad un oggetto I riferimenti sono espressioni ⚫ E’ possibile avere più riferimenti ad uno stesso oggetto Classi ed oggetti predefiniti Modellano componenti e comportamenti del sistema Modellano l’interfaccia grafica di interazione con l’utente Modellano “oggetti” di uso comune, ad esempio Data e Calendario Un esempio: il monitor ⚫ Ci si riferisce a lui mediante il riferimento: System.out public class Program1 { public static void main (String[] arg) { System.out.println("Benvenuti al corso"); } } PrintStream e System.out La classe PrintStream ⚫ Modella monitor e stampanti ⚫ Comportamento: visualizzare sequenze di caratteri System.out ⚫ Un riferimento ad un oggetto predefinito ⚫ Istanza della classe PrintStream PrintStream e System.out XXXXXXX Un oggetto Si riferisce a XXXXXXXXXXXXXXXXXXXXX PrintStream XXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX System.out XXXXX XXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX XXXXXXXXXX XXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX XXXX Un programma Modella Java oppure Messaggi in Java Forma generale Comportamento-desiderato (altre-informazioni) Esempio: println(“Benvenuti al corso”) ⚫ Comportamento: println (stampa una linea) ⚫ Informazione: “Benvenuti al corso” (contenuto della linea) Invio di un messaggio Forma generale: Riferimento-al-destinatario.messaggio Esempio: System.out.println (“Benvenuti al corso”) Riferimento Messaggio ⚫ L’oggetto a cui si riferisce il riferimento System.out è il destinatario del messaggio println(“Benvenuti al corso”) Invio di un messaggio XXXXXXX XXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX System.out.println(“Benvenuti al corso”) XXXXX XXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXX XXXXXXXXXX XXXXXXXXXXXXXXXXXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXXX Un programma Java XXXXXXXXXXXXXXXXXX XXXX Invia un messaggio println(“Benvenuti al corso”) Un messaggio println Benvenuti al corso Modella Destinatario: un oggetto PrintStream Istruzioni Le istruzioni Java ⚫ Provocano un’azione (es: inviare un messaggio) ⚫ Devono essere chiuse da punto e virgola “;” Esempio System.out.println(“Benvenuti al corso”); ⚫ L’invio di un messaggio deve essere sempre espresso da una istruzione Forma di un programma Almeno per le prime lezioni: public class ProgramName { public static void main (String[] arg){ istruzione; istruzione;... istruzione; } } Utilizzare Oggetti Un programma Java o … è un insieme di oggetti, ognuno istanza di una classe, che si inviano messaggi … Percorso formativo Programmare in Java: ⚫ Definire classi ⚫ Istanziare oggetti Imparare ad usare oggetti e classi predefiniti In questa Lezione Imparare a definire nuove classi Tipi e variabili Ogni valore ha un tipo Esempi di dichiarazioni di variabili: String greeting = "Hello, World!"; PrintStream printer = System.out; int luckyNumber = 13; Variabili ⚫ Memorizzano valori ⚫ Possono essere utilizzate per riferirsi ad oggetti Sintassi: Definizione di variabili typeName variableName = value; oppure typeName variableName; Esempio: String greeting = "Hello, Dave!"; Obiettivo: Definire una nuova variabile variableName di tipo typeName e fornire eventualmente un valore iniziale value Identificatori Nome di una variabile, un metodo o una classe Regole in Java: ⚫ Può contenere lettere, cifre e il carattere underscore (_) ⚫ Non può cominciare con una cifra ⚫ Non può contenere altri simboli quali ad esempio ?, %, !, etc. ⚫ Gli spazi non sono consentiti ⚫ Non si possono usare parole riservate di Java ⚫ Maiuscolo/minuscolo sono significativi Convenzioni Per convenzione: ⚫ i nomi delle variabili cominciano per lettera minuscola ⚫ i nomi delle classi cominciano per lettera maiuscola ⚫ nomi composti usano maiuscola ad ogni inizio nuova parola, es: contoCorrente (variabile) ContoCorrente (classe) Assegnamento e valori iniziali Operatore di assegnamento int luckyNumber = 13; luckyNumber = 12; Uso variabili non inizializzate: errore Java! int luckyNumber; System.out.println(luckyNumber); // ERRORE DI COMPILAZIONE // variabile non inizializzata Oggetto Entità di un programma dotata di tre proprietà caratteristiche ⚫ stato ⚫ comportamento ⚫ identità Esempi: ⚫ casella vocale ⚫ conto corrente ⚫ stringa ⚫ studente ⚫ cliente Stato informazioni conservate nell’oggetto ⚫ Casella vocale: vuota, piena, alcuni messaggi ⚫ Conto corrente: saldo nullo, saldo positivo condiziona il comportamento dell’oggetto nel futuro ⚫ Casella vocale: accetta un messaggio se e solo se non piena ⚫ Conto corrente: consente di prelevare se e solo se saldo positivo può variare nel tempo per effetto di un’operazione sull’oggetto ⚫ Casella vocale: aggiunta/cancellazione messaggio ⚫ Conto corrente: versamento/prelevamento Comportamento definito dalle operazioni (metodi) che possono essere eseguite dall’oggetto ⚫ Casella vocale: lettura messaggio, cancellazione messaggio, etc. ⚫ Conto corrente: lettura saldo, versamento, prelevamento, etc. i metodi possono modificare lo stato dell’oggetto ⚫ Casella vocale: aggiunta messaggio può far cambiare lo stato da vuoto ad alcuni messaggi, o da alcuni messaggi a pieno. ⚫ Conto corrente: versamento può far cambiare lo stato da saldo nullo a saldo positivo Classe: concetto astratto Ogni oggetto appartiene a (è un’istanza di) una classe che ne determina il tipo Una classe descrive un insieme di oggetti caratterizzati dallo stesso insieme di ⚫ possibili comportamenti (metodi) ⚫ possibili stati (variabili di istanza o campi) Es. tutte le caselle vocali di un certo tipo appartengono ad una stessa classe Mailbox Possibili stati: le variabili di istanza Le variabili di istanza (campi) memorizzano lo stato di un oggetto Ciascun oggetto di una certa classe ha la propria copia delle variabili di istanza Le variabili di istanza solitamente possono essere lette e modificate solo dai metodi della stessa classe (incapsulamento dei dati) Possibili comportamenti: metodi parte computazionale della classe somigliano a funzioni dei linguaggi procedurali tipo C possono utilizzare altri metodi (anche della stessa classe) e manipolare/accedere il contenuto delle variabili di istanza String greeting = "Hello"; greeting.println(); // Error greeting.length(); // OK Messaggi e metodi Il comportamento di un oggetto è attivato dalla ricezione di un messaggio Le classi determinano il comportamento degli oggetti definendo quali sono i messaggi “leciti” Le classi determinano i messaggi leciti mediante la definizione di metodi: ⚫ Una sezione di codice all’interno di una classe che implementa un particolare comportamento ⚫ Sono individuati da un nome del metodo Forma di un messaggio nome_del_metodo(argomenti) Un messaggio deve specificare ⚫ Il nome del metodo da invocare … il comportamento desiderato ⚫ Gli eventuali argomenti … altre informazioni Argomenti System.out.println (“Benvenuti al corso”) Nome del metodo I metodi di PrintStream Conoscere una classe equivale a conoscerne i metodi La classe: PrintStream Nome Argomenti println stringa di caratteri println nessuno print stringa di caratteri Esempi System.out.println(“Benvenuti al corso”); System.out.println(); System.out.print(“Questa frase va su”); System.out.print(“ una sola linea”); La segnatura di un metodo println(“salve”) e println() sono lo stesso metodo ? Due metodi differenti ⚫ Stesso nome ⚫ Argomenti diversi ⚫ Comportamento diverso La segnatura (signature) di un metodo: Il nome del metodo + la descrizione degli argomenti Overloading I metodi sono individuati dalla segnatura, e non solo dal nome Overloading: la possibilità di avere una classe che definisca metodi differenti con lo stesso nome println è un metodo overloaded della classe PrintStream Invio di un messaggio (I) statement1; statement2; referenceToX.methodA(); OggettoX statement4; methodA Codice methodB methodC Ordine di esecuzione sequenziale Fino a raggiungere una istruzione di invio di un messaggio Invio di un messaggio (II) statement1; statement2; referenceToX.methodA(); OggettoX: receiver statement4; Codice: sender methodA methodB methodC L’esecuzione del sender è sospesa Invio di un messaggio (III) statement1; statement2; referenceToX.methodA(); OggettoX: receiver statement4; Codice: sender methodA() methodA methodB methodC Il messaggio è inviato al receiver Invio di un messaggio (IV) statement1; statement2; referenceToX.methodA(); OggettoX: receiver statement4; Codice: sender methodA() methodA methodB methodC L’arrivo del messaggio provoca l’invocazione di uno dei metodi del receiver Invio di un messaggio (V) statement1; statement2; referenceToX.methodA(); OggettoX: receiver statement4; Codice: sender methodA methodB methodC Il codice relativo al metodo invocato viene eseguito ⚫ Questo può eventualmente provocare l’invio di altri messaggi ad altri oggetti Invio di un messaggio (VI) statement1; statement2; OggettoX: receiver referenceToX.methodA(); statement4; methodA Codice: sender methodB methodC Quando l’esecuzione del metodo invocato termina ⚫ Il controllo (ed eventuali informazioni aggiuntive) viene restituito al sender (return) ⚫ Riprende l’ordine sequenziale La classe String Una classe predefinita Modella una qualunque sequenza di caratteri Referenze ad oggetti String ⚫ Sequenze di caratteri fra doppi apici ⚫ “Benvenuti al corso” Rappresentazione di due oggetti String String: referenze ed oggetti riferimento System.out.println(“Hi!”); … Modella System.out.println(“BYE!”); Hi! riferimento Modella BYE! “Hi!” e “BYE!” sono due riferimenti a oggetti String che modellano le sequenze di caratteri Hi! e BYE! Riferimenti a stringhe esempi di utilizzo Come argomento di un messaggio ⚫ Uno dei metodi println di PrintStream ha un argomento che è un riferimento ad un oggetto stringa ⚫ println(riferimento-ad-un-oggetto- String) un riferimento System.out.println(“Hi!”); Modella Hi! Alcuni metodi di String length(): conta caratteri in una stringa String greeting = "Hello, World!"; int n = greeting.length(); // sets n to 13 toUpperCase(): crea una nuova stringa che contiene i caratteri della stringa originale in maiuscolo String river = "Mississippi"; String bigRiver = river.toUpperCase(); // sets bigRiver to "MISSISSIPPI" Invocazione di un metodo Per invocare un metodo di un certo oggetto bisogna specificare il nome del metodo preceduto dal nome dell’oggetto e da un punto ⚫ Es.: river.length(); (Eseguiamo il metodo length sull’oggetto river) L’oggetto funge da parametro implicito nell’invocazione del metodo ⚫ E’ come passare a length il parametro river Riferimenti a stringhe esempi di utilizzo Invio di un messaggio ad una stringa La classe String offre molti metodi Un esempio: toUpperCase “ibm”.toUpperCase() “ibm” toUpperCase() reference message ibm receiver Il metodo toUpperCase Crea un nuovo oggetto String Tutti i caratteri sono in maiuscolo Restituisce (returns) un riferimento (reference) al nuovo oggetto receiver toUpperCase() ibm message reference L’espressione IBM “ibm”.toUpperCase() nuovo oggetto Si riferisce al nuovo oggetto Self Check Come si può calcolare la lunghezza della stringa "Mississippi"? Come si può stampare la versione uppercase di "Hello, World!"? E’ corretta l’invocazione river.println()? Perché si o perché no? Risposte river.length() or "Mississippi".length() System.out.println(greeting.toUpperCase()); Non è corretto. La variabile river è di tipo String. Il metodo println non è un metodo della classe String. Parametri impliciti ed espliciti Parametri (espliciti): dati in ingresso ad un metodo. Non tutti i metodi hanno parametri espliciti System.out.println(greeting); greeting.length(); // senza parametri espliciti Parametro implicito: Oggetto su cui è invocato il metodo System.out.println(greeting); Valore restituito Un risultato che il metodo ha calcolato e che viene passato al metodo chiamante per essere utilizzato nella computazione di quest’ultimo int n = greeting.length(); // n contiene valore restituito Valore di restituzione Può essere utilizzato come parametro in un messaggio System.out.println(greeting.length()); Un esempio complesso Il metodo replace esegue una operazione di ricerca e sostituzione in una stringa river.replace("issipp", "our"); // costruisce una nuova stringa ("Missouri") Questo metodo ha: ⚫ 1 parametro implicito: la stringa "Mississippi" ⚫ 2 parametri espliciti: le stringhe "issipp" e "our" ⚫ 1 valore restituito: la stringa "Missouri" Definizione di un metodo Specifica il tipo dei parametri espliciti e il valore di restituzione Tipo del parametro implicito = la classe corrente; non è scritto nella definizione del metodo Esempio nella classe String public int length() // return type: int // no explicit parameter public String replace(String target, String replacement) // return type: String; // two explicit parameters of type String Definizione di un metodo void è usato per indicare che il metodo non restituisce alcun valore public void println(String output) // in class PrintStream Il nome di un metodo è sovraccaricato (overloaded) se ci sono più metodi con lo stesso nome nella classe (con parametri differenti) public void println(String output) public void println(int output) Self Check o Quali sono i parametri impliciti, i parametri espliciti, e il valore di ritorno nella chiamata a metodo river.length()? Ricorda che String river= “Mississippi"; o Qual’è il risultato della chiamata river.replace("p", "s")? o Qual’è il risultato della chiamata greeting.replace("World", "Dave").length()? Ricorda che String greeting = "Hello, World!"; o Com’è definito il metodo toUpperCase nella classe String? Risposte Il parametro implicito è river. Non ci sono parametri espliciti. Il valore di ritorno è 11 "Missississi" 12 Come public String toUpperCase(), con nessun parametro esplicito e tipo di ritorno String. Variabili di riferimento Variabile: un identificatore a cui si può attribuire un valore ⚫ “si supponga che x valga 5” ⚫ “posto y pari al valore della temperatura esterna …” ⚫ Radice: variabilità nel tempo Variabile di riferimento: Una variabile il cui valore è un riferimento ad un oggetto Dichiarazione Le variabili di riferimento devono essere dichiarate String greeting; PrintStream output; In generale: classe identificatore; Assegnamento E’ necessario assegnare un valore ad una variabile di riferimento prima di poterla utilizzare Il tipo del valore deve combaciare con il tipo con cui si è dichiarata una variabile (type matching) greeting = “Ciao”; greeting = System.out; In generale: variabile = valore; Il valore è copiato nella variabile Dichiarazione ed assegnamento String greeting; “ciao” greeting = “ciao”; ciao greeting oggetto String String greeting; “ciao” greeting = “ciao”; ciao greeting oggetto String Esempio (I) String s1, s2; PrintStream ps1, ps2; s1 = “hello”; s2 = “goodbye”; s1 = s2; ps2 = System.out; ps1 = ps2; ps1.println(s1); // cosa succede ? Esempio (II) String greeting; greeting = “hey!”; String bigGreeting; bigGreeting = greeting.toUpperCase(); System.out.println(bigGreeting); System.out.println(bigGreeting); System.out.println(bigGreeting); … al posto di … System.out.println(greeting.toUpperCase()); System.out.println(greeting.toUpperCase()); System.out.println(greeting.toUpperCase()); Assegnamento non è uguaglianza String t; t t = “Cash”; Cash oggetto String si riferisce a t t = “Credit”; Cash oggetto String si riferisce a t Credit oggetto String Variabili e oggetti Una variabile di riferimento si riferisce ad un solo oggetto alla volta Un oggetto può essere referenziato da più variabili simultaneamente String s, t; s s=“Pippo”; Pippo t=s; t Ruoli delle variabili Salvataggio o recupero a seconda della posizione String s, t; s = “Pippo”; // s, variabile; “Pippo”, valore t = s; // t, variabile; s, valore Indipendenza: s=“Pluto”; //cambia il valore di s NON di t Dichiarazione (variazioni) Più variabili sulla stessa linea String x,y,z; Con inizializzazione String nome = “Marco”, cognome = “Rossi”; Possono essere distribuite nel codice ma devono precedere l’uso Esempio (I) public class Esempio { public static void main(String arg[]) { String greeting; String bigGreeting; greeting = ”Hi, World"; bigGreeting = greeting.toUpperCase(); System.out.println(greeting); System.out.println(bigGreeting); } } Esempio (II) public class Esempio { public static void main(String arg[]) { String greeting; greeting = ”Hi, World"; String bigGreeting; bigGreeting = greeting.toUpperCase(); System.out.println(greeting); System.out.println(bigGreeting); } } Esempio (III) public class Esempio { public static void main(String arg[]) { String greeting = "Yo, World"; String bigGreeting = greeting.toUpperCase(); System.out.println(greeting); System.out.println(bigGreeting); } } Ancora sulle stringhe Metodi della classe String method returns arguments toUpperCase ref. String object none toLowerCase ref. String object none length a number none trim ref. String object none concat ref. String object ref. String object substring ref. String object number substring ref. String object two numbers Posizioni nelle stringhe Le posizioni dei caratteri in una stringa sono numerate a partire da 0 H a m b u r g e r 0 1 2 3 4 5 6 7 8 Stringhe e sottostringhe String big = “hamburger”; String small = big.substring(3,7); String medium = big.substring(3); String bigInCaps = big.toUpperCase(); String order = big.concat(“ with onions”); Esempio import java.io.*; class Esempio { public static void main(String arg[]) { String first = "John"; String middle = "Fitzgerald"; String last = "Kennedy"; String initials; String firstInit, middleInit, lastInit; firstInit = first.substring(0,1); middleInit = middle.substring(0,1); lastInit = last.substring(0,1); initials = firstInit.concat(middleInit); initials = initials.concat(lastInit); System.out.println(initials); } } Altri metodi della classe String String s = “parola” length(): ritorna la lunghezza della stringa int len = s.length(); // len == 6 charAt(int i): ritorna il carattere in posizione i-esima char c=s.charAt(0); // c ==‘p’ indexOf(char c): ritorna l’indice della prima occorrenza del carattere indicato int i=s.indexOf(‘o’); // i == 3 lastIndexOf(char c): come sopra ma per l’ultima occorrenza di c Proprietà delle stringhe Immutabilità: una volta creato un oggetto String NON può cambiare ⚫ Es: l’invio di un messaggio toUpperCase comporta la creazione di un nuovo oggetto String Stringa vuota ⚫ Lunghezza 0 ⚫ Nessun carattere ⚫ Reference: “” Meccanismi Dato String w, x, y, z, s; w = “ab”; x = “cd”; y = “ef”; z = “gh”; Assegnare ad s la concatenazione delle stringhe referenziate da w, x, y, z ⚫ “abcdefgh” Cascata di messaggi s=w.concat(x).concat(y).concat(z); Il messaggio concat(x) è inviato a w ⚫ L’espressione w.concat(x) si riferisce alla stringa risultante ⚫ w.concat(x) abcd Il messaggio concat(y) è inviato alla nuova stringa “abcd” ⚫ L’espressione w.concat(x).concat(y) si riferisce alla stringa risultante ⚫ w.concat(x).concat(y) abcdef Il messaggio concat(z) è inviato alla nuova stringa “abcdef” ⚫ L’espressione w.concat(x).concat(y).concat(z) si riferisce alla stringa risultante ⚫ w.concat(x).concat(y).concat(z) abcdefgh Cascata di messaggi s=w.concat(x).concat(y).concat(z) E’ il processo di invio di un messaggio ad un oggetto per creare un nuovo oggetto, che a sua volta riceve un messaggio per creare un nuovo oggetto, che … Composizione di messaggi s=w.concat(x.concat(y.concat(z))) Il messaggio concat(z) è inviato a y ⚫ y.concat(z) si riferisce alla stringa risultante ⚫ y.concat(z) efgh Un messaggio concat con tale nuovo oggetto come argomento è inviato a x ⚫ x.concat(y.concat(z)) si riferisce alla stringa risultante ⚫ x.concat(y.concat(z)) cdefgh Un messaggio concat con tale nuovo oggetto come argomento è inviato a w ⚫ w.concat(x.concat(y.concat(z))) si riferisce alla stringa risultante ⚫ w.concat(x.concat(y.concat(z))) abcdefgh Esercizi Scrivere un programma che stampa il carattere centrale di una stringa. Scrivere un programma che data una stringa di almeno 2 caratteri, ne costruisce un’altra dove il primo carattere è scambiato con l’ultimo, che viene poi stampata a video. Esercizio Scrivere un programma che data una stringa costante di almeno 2 caratteri, s1 s2 costruisce la seguente stringa, s2 s1 che viene poi stampata a video. Classe Rectangle Oggetti di tipo rettangolo: Per descrivere un rettangolo posso specificare ⚫ L’ascissa x e l’ordinata y del suo angolo superiore sinistro Stato ⚫ il valore della larghezza (width) ⚫ il valore dell’altezza (height) x=5 x=y=0 width=20 y=10 height=30 Classe Rectangle cont. Operazioni possibili: ⚫ traslazione del punto iniziale: translate(x,y) ⚫ recupero valore altezza: getHeight() ⚫ modifica ampiezza e altezza: resize(w,h) ⚫ intersezione con altro rettangolo: intersection(R) ⚫ test intersezione non vuota: intersects(R) ⚫ test uguaglianza: equals(O) ⚫ etc. Forme rettangolari ed Oggetti Rectangle Un oggetto Rectangle non è una forma rettangolare – ma un oggetto che contiene un insieme di numeri che descrivono il rettangolo Il metodo costruttore ▪ In Java per creare un oggetto di una classe è necessario eseguire il metodo costruttore. ▪ Per la classe String esiste una scorciatoia poiché il costruttore viene invocato quando utilizziamo le doppie virgolette, ad esempio “Corso” ▪ Il costruttore ha sempre lo stesso nome della classe. Esso non deve restituire alcun valore, ma non deve neanche essere dichiarato di tipo void. ▪ Solitamente viene dichiarato di tipo public. Per compiere le operazioni di inizializzazione vengono spesso usati i parametri. Variabili che contengono oggetti La variabile box contiene un riferimento (indirizzo) ad un oggetto di tipo rettangolo. Creazione di oggetti (1) Rectangle box; Definisce una variabile oggetto rettangolo non inizializzata E’ buona norma inizializzare sempre le variabili oggetto box = new Rectangle(5, 10, 20, 30); Crea un rettangolo e assegna il suo indirizzo a box Creazione di oggetti (2) Rectangle box = new Rectangle(5, 10, 20, 30); Rectangle box2 = box; // (assegnamento a variabile) Si ottengono due variabili oggetto che si riferiscono allo stesso oggetto Creazione di oggetti (3) Quando viene creato l'oggetto box con Rectangle box = new Rectangle(5, 10, 20, 30); viene allocato uno spazio di memoria in cui sono conservati ⚫ i valori di x, y, width e height quindi ciascuna istanza di Rectangle ha le proprie copie delle variabili x, y, width e height ⚫ gli indirizzi dei metodi Rectangle, translate, etc. quindi i metodi di tutte le istanze di Rectangle sono implementati con lo stesso codice Creazione di oggetti (4) Rectangle box = new Rectangle(5, 10, 20, 30); Rectangle r = new Rectangle(5, 10, 20, 30); Si definiscono due variabili inizializzate con due oggetti distinti di tipo Rectangle r e box si riferiscono a due oggetti che sono indistinguibili rispetto allo stato (stesso stato) e al comportamento, ma hanno identità differenti Il metodo costruttore public Rectangle(int x_init, int y_init, int width_init, int height_init) { x=x_init; y=y_init; width=width_init; height=height_init; } ▪ Una classe può implementare un metodo particolare, detto costruttore, che serve a inizializzare i nuovi oggetti ▪ Quando esiste un costruttore deve chiamarsi come la classe ▪ Per creare un oggetto di una classe deve essere invocato un costruttore della classe in combinazione con l’operatore new ▪ Se una variabile di istanza non è inizializzata dal costruttore allora è inizializzata automaticamente a 0 se si tratta di un tipo numerico o a null se si tratta di un riferimento Self Check Come costruisci un quadrato con centro (100, 100) e lunghezza del lato 20? Cosa stampa la seguente istruzione? System.out.println(new Rectangle().getWidth()); Risposte new Rectangle(90, 90, 20, 20) 0 Self Check - Identificare gli errori Rectangle r = (5, 10, 15, 20); double width = Rectangle(5, 10, 15, 20).getWidth(); Rectangle r; r.translate(15,25); r = new Rectangle(); r.translate(“far, far away!”); Copiare Numeri int luckyNumber = 13; int luckyNumber2 = luckyNumber; luckyNumber2 = 12; Copiare Riferimenti ad Oggetti Rectangle box = new Rectangle(5, 10, 20, 30); Rectangle box2 = box; box2.translate(15, 25); Self Check o Qual’è l’effetto dell’assegnamento greeting2 = greeting? o Dopo l’invocazione di greeting2.toUpperCase(), quali sono i contenuti di greeting e greeting2? Classificazione metodi Metodi di accesso: non cambiano lo stato del suo parametro implicito double width = box.getWidth(); Metodi modificatori: cambiano lo stato del suo parametro implicito box.translate(15, 25); Self Check o Il metodo toUpperCase della classe String è un metodo di accesso o un modificatore? o Quale chiamata a translate è necessaria per muovere il rettangolo box cosi che il punto in alto a sinistra è l’origine (0, 0)? Rectangle box = new Rectangle(5, 10, 20, 30); box.translate(-5, -10) Garbage Collection Quando un oggetto non ha più un riferimento non può più essere referenziato da alcun programma L’oggetto diventa inaccessibile e quindi inutile ⚫ si chiama garbage (spazzatura) Java effettua una raccolta automatica e periodica degli oggetti inutili (garbage collection) ⚫ per rendere nuovamente utilizzabile per usi futuri lo spazio di memoria che l’oggetto occupava ⚫ Per ridurre lo spazio occupato dallo heap In altri linguaggi è responsabilità del programmatore effettuare il rilascio della memoria occupata da oggetti non referenziati e quindi inaccessibili Implementare un programma test Scrivi una nuova classe con il metodo main All’interno del metodo main costruisci uno o più oggetti Applica i metodi agli oggetti Visualizza i risultati delle chiamate ai metodi Importazione pacchetti Per usare le classi delle librerie o riutilizzare codice proprio può essere necessario importare delle classi: ⚫ le classi Java sono raggruppate in pacchetti ⚫ le classi di libreria si importano specificandone il pacchetto e il nome della classe import java.awt.Rectangle; ⚫ Non è necessario importare le classi del pacchetto java.lang come String e System File MoveTester.java 01: import java.awt.Rectangle; 02: 03: public class MoveTester 04: { 05: public static void main(String[] args) 06: { 07: Rectangle box = new Rectangle(5, 10, 20, 30); 08: 09: // Move the rectangle 10: box.translate(15, 25); 11: 12: // Print information about the moved rectangle 13: System.out.print("x: "); 14: System.out.println(box.getX()); 15: System.out.println("Expected: 20"); 16: 17: System.out.print("y: "); 18: System.out.println(box.getY()); 19: System.out.println("Expected: 35"); 20: } 21: } Esercizio Scrivere un programma che inizializzi una variabile square con un rettangolo il cui angolo superiore sinistro abbia coordinate (10,20) ed i cui lati abbiano lunghezza 40. Sostituire poi il contenuto di square con un rettangolo avente le stesse dimensioni ma angolo superiore sinistro posizionato nel punto (20,20). Esercizio Scrivere un programma che istanzi un oggetto Rectangle e lo stampi. Poi, modificare il programma in modo che sposti il rettangolo stampato prima e lo stampi per altre 3 volte in modo che, se i 4 rettangoli stampati fossero disegnati, formerebbero un unico grande rettangolo Scrivere un programma che istanzi un oggetto Rectangle e ne calcoli l’area e il perimetro. Visualizzare i risultati. Esercizio Consultate le API della classe Rectangle ed identificare il metodo void add(int newx, int newy) Qual’è il risultato delle seguenti operazioni? Rectangle box=new Rectangle(5,10,20,30); box.add(0,0); Esercizio Scrivere un programma che scambi tra loro le lettere ”e” ed “o” in una stringa usando ripetutamente il metodo replace. Mostrare che la stringa “Hello, World!” si trasforma in “Holle, Werld!” Realizzare Classi Percorso formativo Programmare in Java: ⚫ Definire classi ⚫ Istanziare oggetti Imparare ad usare oggetti e classi predefiniti Imparare a definire In questa Lezione nuove classi Astrazione Utile nella descrizione, progettazione, implementazione e utilizzo di sistemi complessi Dettagli trascurabili vengono incapsulati in sottosistemi più semplici che vengono quindi utilizzati come delle scatole nere ⚫ non occorre conoscere il loro funzionamento interno ⚫ basta conoscere l’essenza del concetto che rappresentano e l’interazione con il resto del sistema Ad esempio, un autista per usare un auto non necessita di conoscerne i dettagli ingegneristici, deve solo sapere a cosa serve e ad interagire con essa Un esempio di astrazione: Progettazione di automobili Automobile ⚫ trasmissione ⚫ centralina ⚫ … L’astrazione apporta semplificazione e specializzazione ⚫ Migliora l’efficienza Astrazione in Software Design In precedenza: i programmi manipolavano tipi di dati primitivi come numeri e caratteri Per il programmatore risulta essere molto complicato manipolare molte quantità primitive (si possono facilmente introdurre errori) Soluzione: Incapsulare le computazioni di routine in scatole nere (software) L’astrazione è usata per inventare tipi di dati di più alto livello (utilizzato nel processo di identificazione) ⚫ Nella programmazione OO gli oggetti sono scatole nere Incapsulamento: i programmatori usano un oggetto conoscendo il suo comportamento, ma non la sua struttura interna (utilizzato nell’implementazione) Un esempio di astrazione Porta la stessa visione nel software ⚫ Non solo tipi primitivi e routines Basta conoscere il comportamento esterno, non la struttura interna Classi Il comportamento di un oggetto è descritto da una classe Ogni classe ha ⚫ Un’interfaccia pubblica Insieme di metodi (funzioni) che si possono invocare per manipolare l’oggetto Es.: Rectangle(x_init,y_init,width_init,height_init) metodo dell’interfaccia che crea un rettangolo (costruttore) ⚫ Un’implementazione nascosta codice e variabili usati per implementare i metodi dell’interfaccia e non accessibili all’esterno della classe Es.: x, y, width, height La definizione di una classe public class NomeDellaClasse { definizione di metodo definizione di metodo … } Una classe contiene i suoi metodi (descrivono il comportamento comune alle istanze della classe) Le parentesi graffe aperte e chiuse fungono da delimitatori del contenuto di una classe Un esempio public class Allegro1 { public Allegro1() { } public void ridi() { System.out.println("haha"); } } La definizione di un metodo Prototipo e corpo (body) Le parentesi graffe aperte e chiuse fungono da delimitatori del corpo del metodo tipo del valore di ritorno nome del metodo prototipo public void ridi () { corpo del metodo System.out.println(“ha ha ha”); } La definizione di un costruttore prototipo public Allegro1( ) { corpo del costruttore ….. } Nessun valore di restituzione Il nome coincide con quello della classe Invocato insieme a new per restituire un riferimento ad un oggetto appena creato Utilizzare la classe Allegro1 1. Salvare la classe in un file (Allegro1.java) 2. Compilare la classe (javac Allegro1.java) 3. Scrivere un programma che utilizzi la classe Allegro1 public class UsaAllegro1 { public static void main(String[] args) { Allegro1 x; x = new Allegro1(); x.ridi(); x.ridi(); } } Metodi con argomenti Supponiamo che il mittente del messaggio ridi debba poter specificare la sillaba della risata ⚫ ha, ho, hee … Allegro2 x; x = new Allegro2(); x.ridi("ho"); x.ridi("hee"); Metodi con argomenti dichiarazione del parametro public void ridi(String sillaba ) { System.out.print(sillaba); usi del parametro System.out.println(sillaba); } Il numero e tipo degli argomenti nel messaggio devono coincidere con quelli nel prototipo Esempio con Overloading Notare che il class Allegro2 { metodo ridi public Allegro2() { è overloaded } public void ridi() { System.out.println("haha"); } public void ridi(String syl) { System.out.print(syl); System.out.println(syl); } } Oggetti con memoria Supponiamo di voler fornire al metodo costruttore un argomento String che rimpiazzi “ha” come sillaba di default per la risata Allegro3 x; x = new Allegro3(“ho”); x.ridi(); // hoho x.ridi("hee"); // heehee x.ridi(); // hoho Oggetti con memoria Costruttore: public Allegro3(String syl) … Problemi: ⚫ Gli argomenti di un metodo esistono solo durante l’esecuzione del metodo ⚫ Gli argomenti di un metodo sono visibili solo al metodo Come fare in modo che il metodo ridi possa conoscere il valore della syl ? Fornire agli oggetti la capacità di memorizzazione Variabili di istanza (instance variables) Variabili di istanza Una variabile dichiarata all’interno di una classe ma al di fuori di qualsiasi metodo ⚫ Accessibile a tutti i metodi dell’oggetto ⚫ Lo scopo è di memorizzare le informazioni necessarie ai metodi che devono essere preservate tra diverse invocazioni ⚫ Ciascun oggetto ha le proprie variabili di istanza le quali hanno i propri valori ⚫ Stato di un oggetto: i valori delle variabili di istanza ⚫ Tipicamente sono inizializzate dal costruttore Esempio public class Allegro3 { public Allegro3(String syl) { defaultSyl = syl; } Le variabili di istanza possono essere usate public void ridi() { da tutti i metodi System.out.print (defaultSyl); System.out.println (defaultSyl); } public void ridi(String syl) { System.out.print(syl); System.out.println(syl); } Dichiarazione di una private String defaultSyl; variabile di istanza } …miglioriamo ancora … public class Allegro4 { public Allegro4() { defaultSyl = "ha"; } public Allegro4(String syl) { defaultSyl = syl; } public void ridi() { System.out.print (defaultSyl); System.out.println(defaultSyl); } public void ridi(String syl) { System.out.print(syl); System.out.println(syl); } public void ridi(String s1, String s2) { System.out.print(s1); System.out.println(s2); } private String defaultSyl; } … utilizzatore … public class RidiamoUnPoco { public static void main(String[] a) { System.out.println(“Vivi allegramente!"); Allegro4 x,y,z; x = new Allegro4("yuk"); y = new Allegro4("harr"); z = new Allegro4(); x.ridi(); x.ridi("hee"); y.ridi(); z.ridi(); } } Un approccio metodologico FASE1: Progettazione dell’Interfaccia Pubblica ⚫ Decidere il comportamento che la classe dovrà fornire Identificare i metodi da fornire ⚫ Stabilire in che modo la classe verrà usata Definire l’interfaccia della classe, i prototipi dei metodi ⚫ Scrivere un programma di esempio che utilizza la classe ⚫ Scrivere lo scheletro della classe Prototipi e corpi vuoti FASE2: Implementazione di una classe Progettazione dell’interfaccia pubblica di un conto corrente Comportamento di un conto corrente bancario (astrazione): ⚫ depositare contante ⚫ prelevare contante ⚫ leggere il saldo ⚫ creare un nuovo conto Conto corrente (BankAccount): metodi Metodi della classe ContoCorrente: deposita preleva dammiSaldo Vogliamo poter eseguire le seguenti operazioni: harrysChecking.deposita(2000); harrysChecking.preleva(500); System.out.println(harrysChecking.dammiSaldo()); Definizione di un metodo public void deposita(double somma) {... } public void preleva(double somma) {... } public double dammiSaldo() {... } Sintassi specificatoreAccesso tipoRitorno nomeMetodo(tipoParam nomeParam,...) { corpo del metodo } Definizione di un metodo tipo di dato nome del elenco dei restituito metodo parametri public void preleva(double somma) { specificatore di …… accesso …… corpo del } metodo Lo specificatore di accesso indica la visibilità (scope) del metodo ⚫ public indica che il metodo può essere invocato anche dai metodi esterni alla classe ContoCorrente (e anche da quelli esterni al package a cui appartiene la classe ContoCorrente) Costruttore Un costruttore inizializza le variabili di istanza Il nome del costruttore è il nome della classe public ContoCorrente() { // body--filled in later } Costruttore Il corpo del costruttore è eseguito quando viene creato un nuovo oggetto Le istruzioni del costruttore assegnano valori alle variabili di istanza Ci possono essere diversi costruttori ma tutti devono avere lo stesso nome (overloading) ⚫ il compilatore li distingue dalla lista dei parametri espliciti Nota su overloading (sovraccarico) Più metodi con lo stesso nome ⚫ Consentito se i parametri li distinguono, cioè hanno firme diverse (firma = nome del metodo + lista tipi dei parametri nell’ordine in cui compaiono) ⚫ Il tipo restituito non conta Frequente con costruttori ⚫ Devono avere lo stesso nome della classe ⚫ Es.: aggiungiamo a Rectangle il costruttore public Rectangle(int x_init, int y_init) { x=x_init; y=y_init; } Usato anche quando dobbiamo agire diversamente a seconda del tipo passato ⚫ Ad es., println della classe PrintStream Sintassi costruttore accessSpecifier ClassName(parameterType parameterName,...) { constructor body } Esempio: public ContoCorrente(double saldoIniziale) {... } Scopo: Definire il comportamento del metodo costruttore BankAccount: Interfaccia Pubblica I costruttori e i metodi public di una classe formano l’interfaccia pubblica della classe. public class BankAccount { // Constructors public BankAccount() { // body--filled in later } public BankAccount(double initialBalance) { // body--filled in later } BankAccount: Interfaccia Pubblica // Methods public void deposit(double amount) { // body--filled in later } public void withdraw(double amount) { // body--filled in later } public double getBalance() { // body--filled in later } // private fields--filled in later } Self Check Come possiamo usare i metodi dell’interfaccia pubblica per azzerare il conto harrysChecking? Supponiamo di voler un conto bancario che tiene traccia di un numero di conto oltre al saldo. Come si deve cambiare l’interfaccia pubblica? Risposte harrysChecking.withdraw(harrysChecking.getBalance()) Aggiungere un parametro accountNumber ai costruttori, ed aggiungere un metodo getAccountNumber(). Non c’è bisogno di un metodo setAccountNumber poiché il numero di conto non cambia dopo la sua costruzione. Sintassi definizione di classe accessSpecifier class ClassName { constructors methods fields } Example: public class BankAccount { public BankAccount(double initialBalance) {…} public void deposit(double amount) {…}... } Definizione di una classe File BankAccount.java nome del classe public class BankAccount{.. specificatore di. accesso } specificatore di accesso public indica che la classe BankAccount è utilizzabile anche al di fuori del package di cui fa parte la classe una classe pubblica deve essere contenuta in un file avente il suo stesso nome ⚫ Es.: la classe BankAccount è memorizzata nel file BankAccount.java Commenti per documentazione javadoc public void withdraw(double amount) { // implementation filled in later } public double getBalance() { // implementation filled in later } Commenti alla classe public class BankAccount {... } Fornire commenti per ⚫ ogni classe ⚫ ogni metodo ⚫ ogni parametro esplicito ⚫ ogni valore restituito da una funzione Documentazione Variabili di istanza Contengono il dato memorizzato nell’oggetto Istanza di una classe: un oggetto della classe La definizione della classe specifica le variabili d’istanza: public class BankAccount {... private double balance; } Definizione di una variabile di istanza specificatore tipo della Nome della di accesso variabile variabile private int x; Lo specificatore di accesso indica la visibilità (scope) della variabile ⚫ private indica che la variabile di istanza può essere letta e modificata solo dai metodi della classe dall’esterno è possibile accedere alle variabili di istanza private solo attraverso i metodi pubblici della classe Solo raramente le variabili di istanza sono dichiarate public Il tipo delle variabili di istanza può essere ⚫ una classe, Es.: String ⚫ un array ⚫ un tipo primitivo, Es.: int Accesso variabili di istanza Il metodo deposit di BankAccount può accedere alla variabile di istanza private: public void deposit(double amount) { double newBalance = balance + amount; balance = newBalance; } Accesso variabili di istanza Metodi di altre classi non possono: public class BankRobber { public static void main(String[] args) { BankAccount momsSavings = new BankAccount(1000);... momsSavings.balance = -1000; // ERROR } } Incapsulamento = si nasconde dato e si fornisce l’accesso attraverso i metodi Self Check Supponiamo che ogni BankAccount ha un numero di conto. Cosa bisogna cambiare nelle variabili di istanza? Quali sono le variabili di istanza della classe Rectangle? Risposte Una variabile di istanza private int accountNumber; deve essere aggiunta alla classe private int x; private int y; private int width; private int height; In realtà sono state dichiarate public!! Implementazione Costruttori Contengono istruzioni per inizializzare le variabili di istanza public BankAccount() { balance = 0; } public BankAccount(double initialBalance) { balance = initialBalance; } Implementazione Costruttori BankAccount harrysChecking = new BankAccount(1000); Crea un nuovo oggetto di tipo BankAccount Chiama il secondo costruttore siccome viene passato un parametro Assegna il parametro initialBalance a 1000 Assegna la copia del campo balance del nuovo oggetto creato con initialBalance Restituisce un riferimento ad un oggetto di tipo BankAccount (cioè la locazione di memoria dell’oggetto) come valore della new-expression Salva il riferimento nella variabile harrysChecking Implementazione Metodi Alcuni non restituiscono un valore public void withdraw(double amount) { double newBalance = balance - amount; balance = newBalance; } altri si public double getBalance() { return balance; } Invocazione metodo harrysChecking.withdraw(500); Assegna il parametro amount a 500 Recupera il contenuto del campo balance dell’oggetto la cui locazione è salvata in harrysChecking Sottrae il valore amount da balance e salva il risultato in newBalance Salva il valore di newBalance in balance, sovrascrivendo il vecchio valore The return Statement return expression; or return; Esempio: return balance; Scopo: Specificare il valore che un metodo restituisce ed uscire immediatamente dal metodo. Il valore di ritorno diventa il valore del espressione della chiamata a metodo. File BankAccount.java 01: 05: public class BankAccount 06: { 07: 10: public BankAccount() 11: { 12: balance = 0; 13: } 14: 15: 19: public BankAccount(double initialBalance) 20: { 21: balance = initialBalance; 22: } 23: File BankAccount.java 24: 28: public void deposit(double amount) 29: { 30: double newBalance = balance + amount; 31: balance = newBalance; 32: } 33: 34: 38: public void withdraw(double amount) 39: { 40: double newBalance = balance - amount; 41: balance = newBalance; 42: } 43: 44: File BankAccount.java 48: public double getBalance() 49: { 50: return balance; 51: } 52: 53: private double balance; 54: } Self Check Com’è implementato il metodo getWidth della classe Rectangle? Com’è implementato il metodo translate della classe Rectangle? Risposte public int getWidth() { return width; } Ci sono diverse risposte corrette. Una possibile implementazione è: public void translate(int dx, int dy) { int newx = x + dx; x = newx; int newy = y + dy; y = newy; } Testare una classe Classe Tester: una classe con il metodo main che contiene istruzioni per testare un’altra classe Solitamente consiste in: 1. costruire uno o più oggetti della classe da testare 2. invocare sugli oggetti uno o più metodi 3. stampare a video i risultati delle computazioni File BankAccountTester.java 01: 04: public class BankAccountTester 05: { 06: 10: public static void main(String[] args) 11: { 12: BankAccount harrysChecking = new BankAccount(); 13: harrysChecking.deposit(2000); 14: harrysChecking.withdraw(500); 15: System.out.println(harrysChecking.getBalance()); 16: System.out.println("Expected: 1500"); 17: } 18: } Categorie di variabili Variabili di istanza ⚫ Appartengono all’oggetto ⚫ Esistono finché l’oggetto esiste ⚫ Hanno un valore iniziale di default Variabili locali ⚫ Appartengono al metodo ⚫ Vengono create all’attivazione del metodo e cessano di esistere con esso ⚫ Non hanno valore iniziale se non inizializzate Parametri formali ⚫ Appartengono al metodo ⚫ Vengono create all’attivazione del metodo e cessano di esistere con esso ⚫ Valore iniziale è il valore del parametro reale al momento dell’invocazione Categorie di variabili L’ordine delle dichiarazioni è irrilevante ⚫ Convenzione: i metodi prima delle variabili di istanza Controllo di accesso ⚫ public: consente l’accesso al di fuori della classe ⚫ private: limita l’accesso ai membri della classe ⚫ Si applica sia ai metodi che alle variabili di istanza Regole Java Periodo di vita di una variabile ⚫ Parametri e variabili locali vivono solo durante l’esecuzione di un metodo ⚫ Le variabili di istanza hanno lo stesso periodo di vita dell’oggetto cui appartengono Periodo di vita di un oggetto ⚫ Un oggetto esiste fino a quanto c’è una variabile di riferimento che si riferisce ad esso ⚫ La distruzione è automatica Garbage collector In java, il garbage collector periodicamente recupera la memoria relativa ad oggetti non più referenziati Ciclo di vita harrysChecking.deposit(500); ----------------------------- double newBalance = balance + amount; balance = newBalance; Ciclo di vita delle variabili harrysChecking.deposit(500); double newBalance = balance + amount; balance = newBalance; Ciclo di vita delle variabili harrysChecking.deposit(500); double newBalance = balance + amount; balance = newBalance; Parametro espliciti ed impliciti Parametro implicito: l’oggetto di invocazione Il riferimento this denota il parametro implicito public void withdraw(double amount) { balance = balance - amount; } public void withdraw(double amount) { this.balance = this.balance - amount; } Progettazione ad oggetti Caratterizzazione attraverso le classi delle entità (oggetti) coinvolte nel problema da risolvere (individuazione classi) ⚫ identificazione delle classi ⚫ identificazione delle responsabilità (operazioni) di ogni classe ⚫ individuazione delle relazioni tra le classi dipendenza (usa oggetti di altre classi) aggregazione (contiene oggetti di altre classi) ereditarietà (relazione sottoclasse/superclasse ) Realizzazione delle classi Realizzazione di una classe 1. individuazione dei metodi dell’interfaccia pubblica: ⚫ determinazione delle operazioni che si vogliono eseguire su ogni oggetto della classe 2. individuazione delle variabili di istanza: ⚫ determinazione dei dati da mantenere 3. individuazione dei costruttori 4. Codifica dei metodi 5. Collaudo del codice Programmi Java Un programma Java consiste di una o più classi Per poter eseguire un programma bisogna definire una classe pubblica che contiene un metodo public static void main(String[] args) Esercizio Supponiamo di voler realizzare un contapersone. Ogni volta che un operatore preme un pulsante il valore del conteggio viene incrementato. Esercizio Supponiamo di voler gestire i dati relativi ai modelli in vendita presso un concessionario d’auto. Per ogni modello occorre tener traccia della marca, del nome, della targa, della capacità del serbatoio e del numero dei chilometri che il modello è in grado di percorrere con un litro di carburante. Il titolare del concessionario potrebbe essere interessato a calcolare l’autonomia di ogni modello (in chilometri). Si definisca inoltre una classe TestAuto che permetta di creare 2 automobili e di calcolarne l’autonomia. Tipi di dati fondamentali Outline I tipi primitivi Conversione tra tipi Le costanti I metodi statici della classe Math Lettura dell’input da tastiera La nozione di tipo di dato Il tipo del dato consente di esprimere la natura del dato Indica il modo con cui verrà interpretata la sequenza di bit che rappresenta il dato ⚫ La stessa sequenza può rappresentare un intero o un carattere ad esempio Determina il campo dei valori che un dato può assumere Specifica le operazioni possibili sui dati Tipi di dati e Java Java è un linguaggio fortemente tipizzato Il tipo di ogni variabile o espressione può essere identificato leggendo il programma ed è già noto al momento della compilazione ⚫ È obbligatorio dichiarare il tipo di una variabile prima di utilizzarla ⚫ Durante la compilazione sono effettuati tutti i controlli relativi alla compatibilità dei tipi e sono stabilite eventuali conversioni implicite. Dopo la dichiarazione non è possibile assegnare alla variabile valori di tipo diverso ⚫ Salvo casi particolari che vedremo in seguito Tipi primitivi: interi Java fornisce otto tipi primitivi indipendenti dall’implementazione e dalla piattaforma Interi ⚫ Tipo byte (8 bit) Interi con segno tra -128 e 127, valore di default 0 ⚫ Tipo short (16 bit) Interi con segno tra -32768 e 32767, valore di default 0 ⚫ Tipo int (32 bit) Interi con segno tra -231 e 231-1, valore di default 0 ⚫ Tipo long (64 bit) Interi con segno tra -263 e 263-1, valore di default 0 Tipi primitivi: costanti intere Una costante intera per default è di tipo int Costanti intere possono essere espresse anche in ottale (prefisso 0) o in esadecimale (prefisso 0x) Per costanti intere di tipo long aggiungere il suffisso L oppure l ⚫ Es. 4000L Costanti intere di tipo byte (risp. short) ⚫ costanti di tipo int il cui valore rientra nel range del tipo byte (risp. short) Tipi primitivi: numeri con virgola Seguono standard IEEE 754 Tipo float (32 bit) ⚫ numeri in virgola mobile con 7 cifre significative ⚫ compresi tra 1.4E-45 e 3.4028235E+38 ⚫ valore di default 0.0 ⚫ le costanti vanno terminate con F o f es. float a=3.456F; Tipo double (64 bit) ⚫ numeri in virgola mobile in doppia precisione (15 cifre significative) ⚫ compresi tra 4.9E-324 e 1.7976931348623157E+308 ⚫ valore di default 0.0 ⚫ le costanti con virgola sono di tipo double per default ⚫ possono essere terminate con D o d ma non è necessario Tipi Primitivi: caratteri Seguono la codifica Unicode che estende ASCII su 16 bit Tipo char (16 bit) ⚫ Si possono usare i caratteri o i relativi codici numerici preceduti da \u, sempre tra apici. Es. char a='A'; char a='\u0041'; // (in esadecimale su 4 cifre) char a=65; ⚫ Caratteri con codici tra 0 e 65535 ⚫ valore di default '\u0000' ('\0' del C) ⚫ Possibilità di usare \ per caratteri particolari ('\n', '\t', '\"', '\b', '\0', …) Tipi Primitivi: boolean Tipo boolean (1 bit) Ammette solo due possibili valori (true, false) Valore di default false Non si possono assegnare interi alle variabili booleane ⚫ false non è 0!!! Operatori per i Tipi Primitivi (1) Java ha gli stessi operatori del C, con qualche leggera differenza (+, -, *, /, %, ++, --, +=, -=, *=, /=, %=) ⚫ Non sono applicabili a variabili di tipo boolean Relazionali (, =, ==, !=) ⚫ Producono risultati di tipo boolean (true, false) ⚫ , = non sono applicabili a variabili di tipo boolean Operatori per i Tipi Primitivi (2) Logici (&&, ||, !) ⚫ && and || non valutano espressione destra se valore della condizione può essere stabilita dall’espressione sinistra (valutazione abbreviata) Bit a bit (solo tipi interi e char) ⚫ & (AND), | (OR), ^ (XOR), ~ (complemento bit a bit) ⚫ Shift > (rispetta segno operando), >>> (mette 0 come bit più significativo) Es. x >>= ⚫ ~, , >>>, =, >>>= non si applicano a variabili boolean Tipi delle espressioni Il tipo delle espressioni con operatori aritmetici su interi (ad eccezione degli shift) è int a meno che un operatore è long (in questo caso è long) Per gli operatori di shift non si tiene conto del tipo dell’operando destro Se è presente un operando in virgola mobile il tipo è float a meno che uno degli operandi sia double (e in questo caso è double) Conversione implicita di tipo Ampliamento (da più piccolo a più grande): ⚫ byte → short → int → long → float → double ⚫ char → int → long → float → double Conversione da long a float ⚫ possibile in quanto il range di float è più ampio del range di long ⚫ perdita di precisione (da 64 a 32 bit) Restringimento (da più grande a più piccolo): ⚫ ammesso negli assegnamenti di costanti di tipo int a tipo short, byte o char a patto che il valore della costante possa essere contenuto nel tipo di destinazione Esempi conversioni di tipo int a=1000L; // Errore: tentativo di assegnare long a int (anche se 1000 in int ci va) short s=700; byte b=-70; int x=s+b; // Ok: short e byte sono tipi più piccoli; converte tutto a int come in C float f=1.2; // Errore: assegnazione di double a float (anche se 1.2 in float ci va) double d=700.23; float c=-70F; double x=d+c; // Ok: converte tutto a double come in C float y=d+c; // Errore: converte c a double e tenta di assegnare double a float Virgola mobile e Interi I tipi decimali accettano qualunque tipo di espressione intera, con eventuale arrotondamento sulle cifre meno significative, ma a nessun tipo di intero si possono assegnare espressioni in virgola. Esempi float f=1234567L; // OK: diventa 1234567.0 float d=12345678; // OK: diventa 1.2345678E7 float c=-123456782; // OK: arrotondamento sulle ultime cifre long x=f; // Errore: tenta di assegnare float a long Char e Interi Ai tipi interi long e int si possono assegnare espressioni char che verranno convertite nel relativo codice numerico, a byte e short non si possono assegnare char e a char non si può assegnare nessun intero. Esempi char c='B'; int d=44+c; // OK: c viene convertito a int e d vale 110 char s=d; // Errore: assegna int a char (anche se 110 è un // valore possibile per char) char x=110; // Ok: 110 è un valore possibile per un char char t=-7; // Errore: -7 non è un valore possibile per un char Ancora sulle Conversioni Ai tipi float e double si possono sempre assegnare espressioni char mentre il contrario non è mai possibile. Esempi char c='B'; float d=44+c; // OK: c e 44 vengono convertiti a float e d vale 110.0 IMPORTANTE: Non sono possibili conversioni di tipo da/verso boolean Casting sui Tipi Primitivi Un cast esplicito può servire a forzare le conversioni che in Java non sono permesse. La sintassi è uguale a quella del C. Esempi double d=-1.8345678901234567; float f=(float)d; // perdita di precisione int i=(int)d; // i vale -1 (non c’è arrotondamento) short s=-700; char c=(char)s; // possibile, ma senza senso boolean b=(boolean)i; // Errore: non sono permessi cast da/verso boolean Tabella delle Conversioni La divisione intera Se entrambi gli operandi sono interi allora il risultato della divisione è un intero ⚫ 9 / 4 è 2 e non 2.25! Se si vuole che il risultato sia un numero decimale allora almeno uno degli operandi deve essere un numero in virgola mobile ⚫ 9 / 4.0 è 2.25 Riassumendo Le conversioni tra tipi di dato possono avvenire in 3 modi: a. Conversioni durante una operazione di assegnamento b. Promozione in una espressione aritmetica c. Casting a. quando un valore di un tipo viene assegnato ad una variabile di un altro tipo ⚫ È consentita solo la conversione larga b. avviene automaticamente quando operatori aritmetici devono convertire gli operandi c. tecnica di conversione più pericolosa e potente Tramite un casting esplicito si possono realizzare sia la conversione larga sia quella stretta Variabili In Java le variabili possono essere dichiarate ovunque nel codice ⚫ int a=20; ⚫ int n=a*10; Una dichiarazione consiste in una serie modificatori (opzionale), un tipo e un nome ⚫ La dichiarazione delle variabili di istanza comincia con uno specificatore di accesso (opzionale) Variabili final ⚫ Il loro valore non può essere modificato (costante) ⚫ Possono essere dichiarate in un metodo: final nomeTipo nomeVar = espressione; una classe: specificatoreDiAccesso static final nomeTipo nomeVar = espressione; ⚫ Si usano in genere nomi con caratteri maiuscoli Nota: static denota una variabile della classe, quindi non ne viene creata una copia per ogni oggetto istanziato ma tutti gli oggetti fanno riferimento alla stessa variabile Esempio 01: 04: public class CashRegister 05: { 06: 09: public CashRegister() 10: { 11: purchase = 0; 12: payment = 0; 13: } 14: 15: 19: public void recordPurchase(double amount) 20: { 21: purchase = purchase + amount; 22: } Esempio 23: 24: 32: public void enterPayment(int dollars, int quarters, 33: int dimes, int nickels, int pennies) 34: { 35: payment = dollars + quarters * QUARTER_VALUE + dimes * DIME_VALUE 36: + nickels * NICKEL_VALUE + pennies * PENNY_VALUE; 37: } 38: 39: 43: public double giveChange() 44: { Esempio 45: double change = payment - purchase; 46: purchase = 0; 47: payment = 0; 48: return change; 49: } 50: 51: public static final double QUARTER_VALUE = 0.25; 52: public static final double DIME_VALUE = 0.1; 53: public static final double NICKEL_VALUE = 0.05; 54: public static final double PENNY_VALUE = 0.01; 55: 56: private double purchase; 57: private double payment; 58: } Esempio 01: 04: public class CashRegisterTester 05: { 06: public static void main(String[] args) 07: { 08: CashRegister register = new CashRegister(); 09: 10: register.recordPurchase(0.75); 11: register.recordPurchase(1.50); 12: register.enterPayment(2, 0, 5, 0, 0); 13: System.out.print("Change: "); 14: System.out.println(register.giveChange()); 15: System.out.println("Expected: 0.25"); 16: 17: register.recordPurchase(2.25); 18: register.recordPurchase(19.25); 19: register.enterPayment(23, 2, 0, 0, 0); 20: System.out.print("Change: "); 21: System.out.println(register.giveChange()); 22: System.out.println("Expected: 2.0"); 23: } 24: } Tipi primitivi e oggetti Valori in Java sono oggetti o tipi primitivi ⚫ variabili di un tipo primitivo contengono valori ⚫ variabili oggetto contengono riferimenti a oggetti Assegnamenti ⚫ tra variabili di tipo primitivo viene copiato il valore Es. x = y; // x e y hanno lo stesso valore ma non sono // collegate ⚫ tra variabili oggetto viene copiato il riferimento all’oggetto Es. x = y; // x e y si riferiscono allo stesso oggetto per ottenere una copia di oggetti occorre invocare il metodo clone() ⚫ in alternativa, si può istanziare un nuovo oggetto (con lo stesso valore delle variabili d’istanza) La Classe Math La classe Math del package java.lang contiene una serie di metodi statici (metodi della classe) da utilizzare per calcolare funzioni matematiche sui tipi primitivi. In genere i metodi in Math lavorano su double e restituiscono double, ma questo non è un limite perché un metodo che funziona su double funziona anche su tutti gli altri tipi (numerici). NOTA I metodi in Math non possono essere chiamati su variabili di tipo boolean Metodi statici Non operano su una istanza ⚫ non sono associati ad un oggetto ma alla classe stessa Possono essere invocati senza alcun riferimento ad oggetto Sono definiti come tutti gli altri metodi con l’aggiunta del prefisso static Sono invocati utilizzando il nome della classe ⚫ Il riferimento this NON ha senso ⚫ NON possono accedere variabili di istanza ⚫ NON possono accedere metodi non statici ⚫ POSSONO invocare il costruttore Metodi di Math (1) I principali metodi contenuti nella classe Math sono: ⚫ Valore assoluto (implementato anche per float, int e long) double Math.abs(double x) ⚫ Funzioni trigonometriche double Math.sin(x); double Math.cos(x); double Math.tan(x); double Math.asin(x); double Math.acos(x); double Math.atan(x); Metodi in Math (2) Max e Min (implementati anche per float, int e long) ⚫ double Math.max(double x, double y) ⚫ double Math.min(double x, double y) Potenza, esponenziale, logaritmo naturale e radice quadrata ⚫ double Math.pow(double x, double y) ⚫ double Math.exp(double x) ⚫ double Math.log(double x) ⚫ double Math.sqrt(double x) Metodi in Math (3) Funzioni di arrotondamento ⚫ double Math.ceil(double x) ⚫ double Math.floor(double x) ⚫ long Math.round(double x) Costanti (definite con final e static) ⚫ Math.PI (pi greco) ⚫ Math.E (base dei logaritmi naturali) Invocazione di metodi statici ClassName.MethodName( parameters ) ⚫ Metodo statico: metodo che non opera su un particolare oggetto della classe (non ha il parametro implicito) Esempio: Math.round(3.14) (-b + Math.sqrt(b * b - 4 * a * c)) / (2 * a) Stringhe Sequenza di caratteri Oggetti della classe String Immutabili ⚫ nessun metodo di String modifica lo stato della stringa Stringhe costanti: "Carl" Variabili stringhe: String name = "Carl"; Lunghezza di una stringa: int n = name.length(); Concatenazione String fname = "Harry"; String lname = "Hacker"; String name = fname + lname; name è "HarryHacker" Se un operando di + è una stringa, l’altro è convertito in una stringa: String a = "Agent"; String name = a + 7; La stringa name è "Agent7" Conversioni tra stringhe e numeri Da stringhe a numeri: ⚫ stringa contiene un numero (Es. “19” o “19.5”) int n = Integer.parseInt(str); double x = Double.parseDouble(str); ⚫ La conversione lancia un’eccezione se non viene passata una String che non contiene un numero NumberFormatException (di java.lang) Da numeri a stringhe: String str = "" + n; str = Integer.toString(n); str = Double.toString(d); Programma MakePassword.java public class MakePassword { public static void main(String[] args) { String firstName = "Harold"; String middleName = "Joseph"; String lastName = "Hacker"; // estrai l’iniziale String initials = firstName.substring(0, 1) + middleName.substring(0, 1) + lastName.substring(0, 1); // aggiungi l’età int age = 19; // età dell’utente String password = initials.toLowerCase() + age; System.out.println("Your password is ” + password); } } Leggere l’input da console Esiste l’oggetto System.in (della classe java.io.InputStream) ⚫ Legge solo 1 byte alla volta Una stringa però è costituita da caratteri (Unicode usa 2 byte per carattere) ⚫ Da Java 5.0 si usa la classe Scanner (pacchetto java.util) per leggere l’input da tastiera in maniera più semplice Scanner in = new Scanner(System.in); ⚫ int nextInt() legge il prossimo int da tastiera ⚫ double nextDouble() legge il prossimo double da tastiera ⚫ String nextLine() legge la prossima riga da tastiera (finchè l’utente non preme Enter) ⚫ String next() legge la prossima parola da tastiera (finchè non viene immesso il prossimo spazio bianco) Programma Coins.java import java.util.Scanner; public class Coins{ public static void main(String[] args){ final double PENNY_VALUE = 0.01; final double NICKEL_VALUE = 0.05; final double DIME_VALUE = 0.1; final double QUARTER_VALUE = 0.25; Scanner in = new Scanner(System.in); System.out.println(“Quanti penny hai?"); int pennies = in.nextInt(); System.out.println(“Quanti nickel hai?"); int nickels = in.nextInt(); Programma Coins.java System.out.println(“Quanti dime hai?"); int dimes = in.nextInt(); System.out.println(“Quanti quarter hai?"); int quarters = in.nextInt(); double total = pennies * PENNY_VALUE + nickels * NICKEL_VALUE + dimes * DIME_VALUE + quarters * QUARTER_VALUE; // valore totale delle monete System.out.println("Total value = ” + total); } //chiude il corpo del main } //chiude la definizione della classe Decisioni Istruzione if if (amount FREE_TRANSACTIONS) { double fees = TRANSACTION_FEE* (transactionCount – FREE_TRANSACTIONS); super.withdraw(fees); } transactionCount = 0; } Mettere in ombra variabili istanza Una sottoclasse non ha accesso alle variabili private della superclasse E’ un errore comune risolvere il problema creando un’altra variabile di istanza con lo stesso nome La variabile della sottoclasse mette in ombra quella della superclasse Costruzione di sottoclassi Per invocare il costruttore della superclasse dal costruttore di una sottoclasse uso la parola chiave super seguita dai parametri del costruttore ⚫ Deve essere il primo comando del costruttore della sottoclasse public class CheckingAccount extends BankAccount { public CheckingAccount(double initialBalance) { super(initialBalance); transactionCount = 0; } } Costruzione di sottoclassi Se il costruttore della sottoclasse non chiama il costruttore della superclasse, viene invocato il costruttore predefinito della superclasse (cioè super()) ⚫ Se il costruttore di CheckingAccount non invoca il costruttore di BankAccount, viene impostato il saldo iniziale a zero Conversione da Sottoclasse a Superclasse Si può salvare un riferimento ad una sottoclasse in una variabile di riferimento ad una superclasse: SavingsAccount collegeFund = new SavingsAccount(10); BankAccount anAccount = collegeFund; Il riferimento a qualsiasi oggetto può essere memorizzato in una variabile di tipo Object Object anObject = collegeFund; Conversione da Sottoclasse a Superclasse Non si possono applicare metodi della sottoclasse: anAccount.deposit(1000); //Va bene //deposit è un metodo della classe BankAccount anAccount.addInterest(); // Errore //addInterest non è un metodo della classe BankAccount anObject.deposit(); // Errore //deposit non è un metodo della classe Object Polimorfismo Vi ricordate il metodo transfer: public void transfer(BankAccount other, double amount) { withdraw(amount); other.deposit(amount); } Gli si può passare qualsiasi tipo di BankAccount Polimorfismo E’ lecito passare un riferimento di tipo CheckingAccount a un metodo che si aspetta un riferimento di tipo BankAccount BankAccount momsAccount =...; CheckingAccount harrysChecking =...; momsAccount.transfer(harrysChecking, 1000); l compilatore copia il riferimento all’oggetto harrisChecking di tipo sottoclasse nel riferimento di superclasse other Polimorfismo Il metodo transfer non sa che other si riferisce a un oggetto di tipo CheckingAccount Sa solo che other è un riferimento di tipo BankAccount Polimorfismo Il metodo transfer invoca il metodo deposit. ⚫ Quale metodo? Dipende dal tipo reale dell’oggetto (late binding) ⚫ Su un oggetto di tipo CheckingAccount viene invocato CheckingAccount.deposit( ) Vediamo un programma che chiama i metodi polimorfici withdraw e deposit File BankAccount.java 01: 05: public class BankAccount 06: { 07: 10: public BankAccount() 11: { 12: balance = 0; 13: } 14: 15: 19: public BankAccount(double initialBalance) 20: { 21: balance = initialBalance; 22: } 23: File BankAccount.java 24: 28: public void deposit(double amount) 29: { 30: balance = balance + amount; 31: } 32: 33: 37: public void withdraw(double amount) 38: { 39: balance = balance - amount; 40: } 41: 42: File BankAccount.java 46: public double getBalance() 47: { 48: return balance; 49: } 50: 51: 56: public void transfer(double amount, BankAccount other) 57: { 58: withdraw(amount); 59: other.deposit(amount); 60: } 61: 62: private double balance; 63: } File CheckingAccount.java public class CheckingAccount extends BankAccount { public CheckingAccount(double initialBalance) { // chiama il costruttore della superclasse super(initialBalance); // inizializza il conteggio delle transazioni transactionCount = 0; } File CheckingAccount.java //metodo sovrascritto public void deposit(double amount){ transactionCount++; // ora aggiungi amount al saldo super.deposit(amount); } //metodo sovrascritto public void withdraw(double amount){ transactionCount++; // ora sottrai amount dal saldo super.withdraw(amount); } File CheckingAccount.java //metodo nuovo public void deductFees(){ if (transactionCount > FREE_TRANSACTIONS){ double fees = TRANSACTION_FEE * (transactionCount – FREE_TRANSACTIONS); super.withdraw(fees); } transactionCount = 0; } private int transactionCount; private static final int FREE_TRANSACTIONS = 3; private static final double TRANSACTION_FEE = 2.0; } File SavingsAccount.java public class SavingsAccount extends BankAccount{ public SavingsAccount(double rate){ interestRate = rate; } File SavingsAccount.java public void addInterest() { double interest = getBalance() * interestRate / 100; deposit(interest); } private double interestRate; } File AccountTest.java public class AccountTest{ public static void main(String[] args){ BankAccount momsSavings = new SavingsAccount(0.5); BankAccount harrysChecking = new CheckingAccount(100); momsSavings.deposit(10000); momsSavings.transfer(2000, harrysChecking); harrysChecking.withdraw(1500); harrysChecking.withdraw(80); File AccountTest.java momsSavings.transfer(1000, harrysChecking); harrysChecking.withdraw(400); // simulazione della fine del mese ((SavingsAccount) momsSavings).addInterest(); ((CheckingAccoount) harrysChecking).deductFees(); System.out.println("Mom’s savings balance = $" + momsSavings.getBalance()); System.out.println("Harry’s checking balance = $" + harrysChecking.getBalance()); } } Ereditarietà Fattorizzazione Abbiamo visto l’ereditarietà usata per estendere le funzionalità di una classe L’ereditarietà può essere anche usata per spostare un comportamento comune a due o più classi in una singola superclasse Un esempio: un sistema di inventario ⚫ Obiettivi (Lens) ⚫ Pellicole (Films) ⚫ Macchine fotografiche (Cameras) Proprietà Lens ⚫ Focal length ⚫ Zoom/ fixed lens Film ⚫ Recommended storage temperature ⚫ Film speed ⚫ Number of exposures Camera ⚫ Lens included? ⚫ Maximum shutter speed ⚫ Body color Proprietà di tutti gli oggetti inventariati Description Inventory ID Quantity on hand Price Classe Obiettivo public class Lens { public Lens(...) {...} // Constructor public String getDescription() {return description;}; public int getQuantityOnHand() {return quantityOnHand;} public int getPrice() {return price;}... // Methods specific to Lens class... private String description; private int inventoryNumber; private int quantityOnHand; private int price; private boolean isZoom; private double focalLength; } Classe Film public class Film { public Film(...) {...} // Constructor public String getDescription() {return description;}; public int getQuantityOnHand() {return quantityOnHand;} public int getPrice() {return price;}... // Methods specific to Film class... private String description; private int inventoryNumber; private int quantityOnHand; private int price; private int recommendedTemp; private int numberOfExposures; } Classe Camera public class Camera { public Camera(...) {...} // Constructor public String getDescription() {return description;}; public int getQuantityOnHand() {return quantityOnHand;} public int getPrice() {return price;}... // Methods specific to Camera class... private String description; private int inventoryNumber; private int quantityOnHand; private int price; private boolean hasLens; private int maxShutterSpeed; private String bodyColor; } Estrazione di un comportamento comune e fattorizzazione in una superclasse Notare l