Introduzione a Java Concetti base Eclipse, ereditarietà e interfacce Il problema del progetto • I linguaggi classici (C, Pascal, …) ❒ ❒ perfetti per problemi “piccoli”, facilmente scomponibili in funzioni di base (es: registratore di cassa, ecc…) non offrono strumenti mentali adatti ad affrontare la progettazione di sistemi software complessi (es: gestionali di magazzini, ospedali, ecc…). • L’uomo non è portato a pensare il mondo in termini di funzioni… semmai di oggetti che svolgono funzioni. Cos’è Java? • Linguaggio di programmazione: ❒ ❒ ❒ ❒ di alto livello orientato agli oggetti progettato per essere il più possibile indipendente dal SO … e per semplificare l’attività di progetto. Architettura run-time delle applicazioni java • Un’ applicazione Java è strutturata come un insieme di componenti. ❒ ❒ Alcuni sono statici (vengono caricati prima dell’inizio del programma ed Classi esistono per tutta la durata dello stesso) Altri sono dinamici (creati durante Oggetti l’esecuzione, al momento del bisogno) • Poichè un’ applicazione deve avere un punto di partenza prestabilito, uno dei componeneti statici contiene il main main Inversione del punto di vista • Questo appoccio porta a invertire il punto di vista: ❒ ❒ l’enfasi non è più sull’operazione svolta (da qualcuno) ma sull’entità che la svolge • In C l’enfasi è sull’OPERAZIONE, il “chi” la svolge è un dettaglio. (VISIONE PROCEDURALE) printf(“Hello!”);! • In Java (APPROCCIO A OGGETTI) il punto di vista si inverte: l’enfasi si sposta su CHI offre il servizio. • Per esprimere questo cambiamento si usa la notazione puntata: component.operation(param); ! System.out.println(“Hello!”);! • System.out è il componente, println è l’operazione (metodo) il main dal C al Java file myprogram.c int main(int argc, char* argv[]){ …}! per convenzione ogni classe deve ! file MyProgram.java esser definita dentro un file con lo stesso nome public class MyProgram{! public static void main(String[] args){! …! classi devono avere }! Le in Java il main deve essere l’iniziale maiuscola sempre scritto dentro una }! classe “public” Compilazione: dal mondo C… myprogram.c Programma sorgente C (testo) Librerie stdio.h string.h … Compilatore C e linker alle librerie -$ gcc myprogram.c –o program.exe program.exe è: program.exe • scritto in linguaggio macchina (illeggibile dall’uomo) • eseguibile dal SO su cui l’abbiamo compilato: -$ ./program.exe arg1 arg2… S. O. Hardware Compilazione: … al nuovo mondo: Java MyProgram.java Programma sorgente Java (testo) Java Compiler -$ javac MyProgram.java ! Java Virtual Machine (JVM) SO SO SO HW HW HW consegnamo il .class MyProgram.class è: • scritto in bytecode (illeggibile dall’uomo) all’esame? …NOOO! • eseguibile dall’infrastruttura Java, indipendentemente dal SO! !-$ java MyProgram arg1 arg2 …! Esempio 1 – Hello Pinco Pallino! • Creare un’applicazione java che prenda in ingresso due parametri: ❒ <nome cognome> di chi esegue il programma • Stampi a video: Hello <nome cognome> !!! Esempio 1 – Soluzione dal C al Java file myprogram.c include <stdio.h>! int main(int argc, char* argv[]){! printf(“Hello %s!!!“, argv[1]);! }! ! file MyProgram.java public class MyProgram{! public static void main(String[] args){! System.out.println(“Hello ”+args[0]+”!!!”);! }! 0 è il primo parametro, non il }! nome del programma, come in C Semplifichiamoci la vita …e usiamo Eclipse! (www.eclipse.org) • Creare un nuovo progetto: ❒ ❒ ❒ tasto dx su Package Explore New -> Java Project Inserire il nome (es: MyProject) e Finish com’è fatta • Creare una nuova classe: ❒ ❒ ❒ tasto dx sul nome del progetto New -> Class Inserire il nome (es: MyProgram) e Finish la cartella del progetto che Eclipse ci ha creato? • Eclipse crea già la nostra prima classe public class MyProgram{ … }! Compilazione e esecuzione con cosa succede nella Eclipse • compiliamo: Project -> Build All ❒ cartella del progetto? cosa leggo aprendo il .class?! Eclipse fa per noi: -$ javac MyProgram.java! • eseguiamo: ❒ senza parametri: • tasto dx sul .java che contiene il main -> Run as -> Java Application ❒ …ma noi dobbiamo eseguire passando dei parametri: • Run -> Run Configurations… -> doppio click su Java Application • indichiamo il nome della classe che contiene il main • tab Arguments -> scrivo i miei parametri separati da spazi in Program Arguments ❒ Eclipse fa: ! -$ java MyProgram.class “Daniela Loreti” ! I tipi primitivi in Java (1/2) • char • boolean ❒ ❒ (2 byte) (1bit) UNICODE true/false boolean è un tipo autonomo. Non si convertono boolean in interi, neanche con un cast. le espressioni relazionali e logiche (es: if (x>2) …) danno come risultato un boolean, (non un int come in c) Interi: • byte • short • int • long (1byte) (2byte) (4byte) (8byte) -128…+127 -32768…+32768 -2x109…+2x109 -9x1018…+9x1018 I tipi primitivi in Java (2/2) Reali: • float (4byte) -1045…+1045(6-7 cifre sign.) • double (8byte) -10328…+10328(14-15 cifre sign.) Esempio di guai: • float f = Math.sin(Math.PI/3);! Errore di compilazione: “Possible loss of precision, found float, required double” oppure “Type mismatch: cannot convert from double to float” • float f = (float) Math.sin(Math.PI/3);! CAST ESPLICITO. Compilazione OK! Il progettista si assume esplicitamente la responsabilità della perdita di precisione. Esempio 2 – Il contatore Creare un’applicazione java che simuli un conta-persone. • Quando occorre (es: fine giornata) deve poter essere resettato. • Chiunque lo richieda deve poter sapere quante persone sono passate. Comportamento osservabile: è un componente caratterizzato da: ❒ un valore (intero variabile nel tempo) ❒ e tre operazioni: reset() ; inc() ; getStato() Vorrei creare un componente riutilizzabile da diversi main Il contatore in C Come faccio in C? file mycounter.c stato inaccessibile dall’esterno del file. Così nessuno potrà mai usare male il mio contatore nel suo main! static int stato; ! void reset(void){stato=0};! void inc(void){stato++;};! void getStato(void){return stato};! ! esempio di file mymain.c file mycounter.h void reset(void);! void inc(void);! void getStato(void);! ! #include <stdio.h>! #include “mycounter.h”! int main(int argc, char* argv[]){! reset(); inc(); int v = getStato();! printf(“il contatore attualmente vale %d”,v); ! }! ! Il contatore in Java Come faccio in Java? stato inaccessibile dall’esterno della classe public class Contatore{! file Contatore.java private int stato = 0;! attributo public void reset(){stato=0;}! public void inc(){stato++;}! metodi public int getStato(){return stato;} ! }! ! public class MyProgram{! esempio di file MyProgram.java public static void main(String[] args){! Contatore c = new Contatore();! costruttore di default c.reset(); c.inc(); int val = c.getStato();! System.out.println(“il contatore vale: ”+val);! }! è molto più semplice }! stampare un valore E se volessi usare contemporaneamente due contatori? In C mi servirebbero due variabili stato => Mi servirebbe poter importare 2 volte nel main il file mycounter.c. int stato; ! void reset(void){stato=0};! void inc(void){stato++;};! void getStato(void){return stato};! ! ma non lo posso fare …qualcosa mi suggerisce che ho sbagliato tutto! forse …Avrei dovuto usare typedef! E se volessi usare contemporaneamente due contatori? In Java: esempio di file MyProgram.java public class MyProgram{! public static void main(String[] args){! Ah! lo posso fare due Contatore c1 = new Contatore();! volte?! Contatore c2 = new Contatore();! c1.reset(); c1.inc(); int val1 = c1.getStato();! c2.reset(); c2.inc(); int val2 = c2.getStato();! System.out.println(“il contatore 1 vale: ”+val1);! System.out.println(“il contatore 2 vale: ”+val2);! }! Il Contatore non è solo un insieme di funzioni e variabili. }! E’ un TIPO DI DATO! allora lo posso fare anche in C (1/2) file mycounter.c file mycounter.h #include “mycounter.h”! typedef int void reset(contatore* pc){*pc=0};! contatore; ! void inc(contatore* pc){(*pc)++;};! …! void getStato(contatore c){return c};! ! ! esempio di file mymain.c #include <stdio.h>! #include “mycounter.h”! int main(int argc, char* argv[]){! int v1, v2; contatore c1, c2;! reset(&c1); reset(&c2); inc(&c1); v1=getStato(c1);! printf(“il contatore 1 attualmente vale %d”, v1); …! }! ! allora lo posso fare anche in C (2/2) Definire un tipo di dato in C tramite typedef è possibile, ma: • non c’è unitarietà tra parte-dati (espressa con typedef nel mycounter.h) e parte-operazioni (scritte successivamente e altrove, ad es. nel mycounter.c) • non c’è protezione dall’uso improprio, perchè tutti vedono typedef e dunque tutti possono aggirarla • le signature delle operazioni fanno trasparire dettagli (I puntatori…) che non dovrebbero entrare in gioco a questo livello! => il livello di espressività di C non è adeguato! Oggetti Java come istanze di Classi Gli oggetti java sono componenti sw DINAMICI, creati a immagine e somiglianza di una classe • si possono creare tutte le istanze che servono • la creazione avviene run-time al momento del bisogno OGGETTI Contatore, ISTANZE della classe Contatore! classe String • In java le stringhe non sono più array di caratteri. Sono OGGETTI, istanze della CLASSE String! • Java lo sottolinea impedendo di applicare gli operatori classici degli array, come []: per selezionare il carattere i-esimo si usa charAt! String s = “pippo”;! char c = s.charAt(i); //c=“i”! ! • ci sono tantissime operazioni invocabili sulle stringhe => usare il suggest di Eclipse: ❒ nell’esempio precedente, dopo aver digitato “s.” provare la combinazione ctrl+Space) Contatore con 2 costruttori public class Contatore{! file Contatore.java private int stato;! i costruttori public Contatore(){stato = 0;};! sono metodi public Contatore(int val){stato = val};! particolari che public void reset(){stato=0;}! dicono come devono essere public void inc(){stato++;}! inizializzati gli public int getStato(){return stato;} ! oggetti }! ! public class MyProgram{! esempio di file MyProgram.java public static void main(String[] args){! Contatore c1 = new Contatore();! Contatore c2 = new Contatore(10);…! }! Chiama il secondo costruttore. c2 è inizializzato a 10 }! la keyword this Oppure: public class Contatore{! file Contatore.java private int stato;! public Contatore(){};! public Contatore (int stato){this.stato = stato};! public void reset(){stato=0;}! public void inc(){stato++;}! public int getStato(){return stato;} ! denota l’oggetto corrente. di }! norma ! sottintesa, ma può essere specificata se occorre. Riferimenti • in C si possono definire, per ciascun tipo: ❒ ❒ sia variabili (es: int x;) sia puntatore (es: int* px;) • in Java il linguaggio impone due scelte: ❒ ❒ varabili per tipi primitivi (es: int x;) passaggio di parametri per VALORE riferimenti per gli oggetti (es: Contatore c;). passaggio per RIFERIMENTO Riferimenti: cosa ci si può fare • Definirli senza inizializzarli Contatore c; • Assegnare loro la costante null c=null;! questo riferimento non punta a nulla • Usarli per creare nuovi oggetti ! ! ! ! !c = new Contatore();! c • Assegnarli uno all’altro: !Contatore c2 = c;! c2 oggetto c2 adesso referenzia lo stesso oggetto di c Contatore • Confrontarli: c == c2 ! è vera se puntano allo stesso oggetto, NON se hanno lo stesso valore! gli array di java Gli array in java sono oggetti, istanze di una classe speciale denotata da [] Quindi come per ogni oggetto: • prima di definisce un riferimento int[] v;! Contatore[] w; ! • poi si crea dinamicamente l’istanza v = new int[3];! w = new Contatore[6]; ! gli array di java nel primo caso ogni elemento dell’array è una variabile normale, tipo primitivo: v = new int[3]; v[0]=4; v[2]=34;! ! v 34 4 nel secondo caso ogni elemento dell’array è un riferimento a oggetto: w = new Contatore[6];! w[0] = new Contatore(10); ! ! w ! 10 Ereditarietà Spesso capita di aver bisogno di un componente simile a uno già esistente, ma non identico. es: voglio un contatore che sappia anche decrementare! public class ContatoreDec extends Contatore {! public void dec(){stato--;}! file ContatoreDec.java }! ! public class Contatore{! file Contatore.java protected int stato;! con private la variabile stato non public Contatore(){}; …! sarebbe visibile in ContatoreDec }! MyProgram.java c2=new ContatoreDec(30);! c2.inc(); ! Posso invocare tutti I metodi public di c2.dec();! Contatore e ContatoreDec! ! Ereditarietà – tutto è Object! In Java tutte le classi ereditano implicitamente da una classe di sistema: Object. Da essa ereditano alcuni metodi di default: c1 • boolean equals(Object o);! c2 oggetto c1.equals(c2); equivalente a (c1==c2). Contatore restituisce true se sono = i riferimenti. • String toString();! implicitam. chiamato Contatore.toString() System.out.println(“c=”+c); c=Contatore@38da9246! System.out.println(“c=”+c.getStato()); c=24! Integer.toString() Ereditarietà – tutto è Object! ma questi metodi di Object possono essere ridefiniti. basta aggiungerli tra i metodi del Contatore: Lo controllo public class Contatore{ …! perchè public boolean equals(Object obj){! Object obj potrebbe esser if (obj instanceof Contatore) ! qualunque cosa return ((Contatore)obj).getStato() == stato;! else return false;! }! public String toString(){! return “Questo contatore vale ”+this.stato;! }! System.out.println(“c=”+c); ! ora stampa: c=Questo contatore vale 24 }! Classi astratte • Moltissime entità che usiamo per descrivere il mondo non sono reali, ma sono utili per esprimersi. • es: Gli Animali ❒ parlare di “animali” ci è molto utile, ma a ben pensarci non esiste il generico animale! ❒ nella realtà esistono solo animali specifici: “gatto”, “cane”, “serpente” … • per questo esistono le classi astratte! Sono come le classi normali, ma da qualche parte esprimiamo la convenzione di non crearne mai istanze ❒ ❒ public abstract class Animale{ … }! ci posso metter dentro metodi e attributi comuni agli animali Classi astratte …esempio concreto public abstract class Animale{! l’attributo mioNome private String mioNome;! ce l’hanno tutti! Cambia il suo valore! protected String verso;! protected Animale(String s){mioNome=s;}! public abstract String nomeComune();! metodi public abstract String siMuove();! astratti public abstract String doveVive();! public void mostra(){! System.out.println(mioNome+”: è un “ +nomeComune()+”, si muove ”+siMuove()+” e vive ” +doveVive());! mostra() usa i metodi astratti anche se nessuno ha ancora definito }! cosa fanno. POLIMORFISMO }! Ereditarità e classi astratte • Chiunque estenda una classe (normale o astratta) vedrà i suoi metodi e attributi protected e public. • Posso ereditare a cascata da classi (normali o astratte) -> le classificazioni sono spesso ad albero. • Java NON supporta l’ereditarietà multipla -> posso estendere una sola classe Altro esempio • Voglio modellare le classi Studente e Lavoratore. • Presumibilmente le farò estendere una classe Persona! • ma come modello uno StudenteLavoratore? Interfacce • Certe volte mi fa proprio comodo definire solo le operazioni che una certa classe di oggetti può compiere. • spesso occorre ereditarietà multipla …ma senza mescolare le implementazioni!!! public interface IStudente{public int getMatricola();}! public interface ILavoratore{! solo i nomi delle operazioni… public int getStipendio();! non mi interessa (e non posso) dire qui cosa fanno! }! public class Studente implements IStudente{…}! public class Lavoratore implements ILavoratore{…}! public class StudenteLavoratore ! ! ! !implements IStudente, ILavoratore{…}! !Devo definire le operazioni di tutte le !interfacce che implemento! Esercizio 3 - Corsa Mista Implementare un’applicazione java che simuli una gara di corsa tra concorrenti “molto diversi” Tutti i concorrenti hanno un nome proprio, una matricola (numero intero) e una velocità (float, supponiamola costante). Per tutti i concorrenti deve esser possibile: • stampare una breve descrizione • calcolare il tempo di percorrenza data una certa distanza Corsa Mista Possono gareggiare: • concorrenti inquinanti (auto e moto): che usano un certo tipo di carburante, hanno una marca e sono dotati di una certa autonomia in km, esaurita la quale, non potendo rifornirsi, devono ritirarsi. ❒ Le auto hanno anche un numero di passeggeri (da 1 a 5) • concorrenti ecologici (bicicletta e cavallo) Corsa Mista Le biciclette e tutti i veicoli inquinanti sono a conducente umano e come tali devono riposarsi per R ore ogni G ore di guida. R = random tra 6 e 8 G = random tra 10 e 12 calcolaOreDiSonno() calcolaOreDiGuida() • Le auto con più di un passeggero tuttavia non hanno bisogno di interrompere la gara. • I cavalli non dormono mai! Si simuli una corsa per T Km con numero di partecipanti a piacere (anche più concorrenti dello stesso tipo) • si stampi a video la descrizione di ciascuno in ordine di arrivo! Esercizio 3 - Suggerimenti • non esiste il generico Concorrente, ConcorrenteInquinate o ConcorrenteEcologico, come non esisteva il generico Animale • auto, moto, cavalli e biciclette possono essere divisi secondo due classificazioni: ❒ ❒ Inquinanti (auto, moto) – Ecologici (bici, cavallo) ConducenteUmano (auto, moto, bici) – ConducenteAltro (cavallo) • Con una semplice gerarchia di classi non posso mappare questa realtà perchè non esiste l’ereditarietà multipla. • Posso definire un insieme di operazioni che solo auto, moto e bici devono fare? -> se si, posso definire l’interfaccia IConducenteUmano! Esercizio 3 - Suggerimenti • generare un numero random con Java: Math.random() restituisce un double tra 0 e 1. Quindi se invece voglio un int tra min e max: (int) min + Math.random()*(max-min+1) • cercate di replicare meno codice possibile. Es: se due classi devono compiere la stessa operazione, possiamo scrivere il metodo corrispondente una volta sola in una superclasse concreta o astratta che sia.