Paola Magillo, Univestita' di Genova, Corso di Programmazione II per SMID, a.a. 2007-2008.

Laboratorio 00:

RETTANGOLO E CUBOIDE - PRIMA PUNTATA

Vedere dispense per come usare Java in laboratorio.

Nota: Esercizio 1 va fatto per primo, esercizi 3 e 4 possono essere fatti senza esercizio 2, esercizio 4 va fatto dopo 3.

Esercizio 1 - Classi

Prendere la classe Rectangle che rappresenta i rettangoli (ved. dispense).

Provare a compilare ed eseguire.

Sostituire il "main" della classe con quello che legge le dimensioni del rettangolo da command-line (ved. dispense).

Compilare ed eseguire.

Esercizio 2 - Variabili e costanti di classe

(Teoria sulle dispense - "Attributi di istanza e attributi di classe").

Normalmente quando una variabile (o attributo) e' definito in una classe, ogni oggetto della classe ha la sua propria copia di quella variabile. Es. "length" e "width" del rettangolo. Queste si chiamano "variabili di istanza" (appartengono alle singole istanze / oggetti della classe).

Una variabile di cui esiste una copia sola condivisa da tutti gli oggetti della classe si chiama "variabile di classe". Per dire che una variabile e' di classe, si premette la parola chiave "static".

La seguente linea inserita nella classe Rectangle

static int minimumDim = 0;
definisce la variabile di classe "minimumDim" inizializzata a zero.

Aggiungere questa linea nella classe Rectangle.

Definire, sempre in Rectangle, due funzioni con intestazione

void setLength(int l)
e
void setWidth(int w)
che assegnano "length = l;" e "width = w;" dopo aver controllato che il valore sia maggiore o uguale a minimumDim, altrimenti non fanno nulla.

Aggiungere nel "main" due chiamate a queste funzioni, una con valore maggiore e l'altra con valore minore di minimumDim. Stampare il rettangolo dopo ogni chiamata per verificare che Java abbia eseguito o non eseguito l'assegnazione.

La variabile "minimumDim" e' di classe, quindi possiamo accederla usando il nome della classe (es. per cambiare il valore "Rectangle.minimumDim = 10;"). Possiamo anche usare il nome di un qualsiasi oggetto della classe (es. "r.minimumDim = 10;" se r rettangolo).
Siccome siamo dentro alla classe Rectangle, in realta' possiamo anche non usare niente: Java sa che deve cercare la variabile in questa classe (es. "minimumDim = 10;"). Per variabili di altre classi non sarebbe possibile.

Provare tutte queste possibilita' nel codice. Compilare ed eseguire.

Trasformare la variabile "minimumDim" in costante, usando la parola chiave "final".
Per convenzione le costanti in Java si chiamano con nomi tutti maiuscoli, quindi sara' "MINIMUM_DIM".

Nota: Adesso che e' una costante, non e' piu' permesso assegnare un valore a MINIMUM_DIM dopo la sua inizializzazione. Provare.

Esercizio 3 - Ereditarieta'

(Teoria sulle dispense - "Classi e sottoclassi").

Una classe che e' sotto-classe di (o estende) un'altra classe (detta super-classe) eredita dalla super-classe tutte le variabili e funzioni, ne aggiunge di nuove, e puo' ridefinire l'implementazione di alcune funzioni ereditate.

La sintassi per definire una sotto-classe e':

class ...Nome_della_sotto_classe... extends ...Nome_della_super_classe...
{
  ...definizione_nuove_variabili...
  ...definizione_nuove_funzioni...
  ...eventuale_ri_definizione_funzioni_ereditate...
}
Nota: non occorre riscrivere le variabili ereditate ne' le funzioni ereditate (a meno che non si cambi l'implementazione).

Scrivere la classe Cuboid che estende Rectangle per rappresentare i parallelepipedi (o "cuboidi"). Rispetto a Rectangle, questa classe:

Suggerimento: guardate la sintassi con cui sono definite le variabili e le funzioni nella classe Rectangle e copiate cambiando quello che occorre cambiare.

Nella classe mettere anche un "main" che crea un cuboide e ne calcola il volume, stampa il cuboide e il suo volume (analogo per cuboidi del "main" della classe Rectangle).

Esercizio 4 - Binding

Il "binding" e' il meccanismo usato da un linguaggio di programmazione per risalire dal nome di una funzione al codice da eseguire quando e' chiamata quella funzione.

Il problema del binding si pone in Java quando una funzione ereditata da una superclasse e' stata ridefinita nella sotto-classe (es. la funzione "print" in Rectangle e in Cuboid).
In questo caso infatti ci sono due implementazioni e bisogna scegliere quale eseguire.
In Java "vince" sempre la classe dell'oggetto sul quale chiamo la funzione, anche se questo oggetto fosse dentro a una variabile della super-classe (es. un oggetto Cuboide dentro una variabile dichiarata come Rectangle).

Provare nella classe Cuboid questo "main":

{
  Rectangle r1 = new Rectangle();
  Cuboid c1 = new Cuboid();
  Rectangle r2;
  Cuboid c2;
  System.out.println("Metto in r2 un rettangolo e stampo");
  r2 = r1; // ora la variabile contiene un rettangolo
  r2.print();
  System.out.println("Metto in r2 un cuboide e stampo");
  r2 = c1; // ora la variabile contiene un cuboide
  r2.print();
}

E' possibile mettere un oggetto istanza della sotto-classe (Cuboid) dentro una variabile della super-classe (Rectangle). Nel "main" precedente l'abbiamo fatto (ved. linea "r2 = c1").
Il contrario (cioe' es. "c2 = r1") non e' permesso.

Pero' lo posso forzare usando una conversione di tipo esplicita (cast):

c2 = (Cuboid)r1;
Che funziona solo se la variabile r1, pur essendo della super-classe, in questo momento contiene un oggetto della sotto-classe. Altrimenti produce errore run-time.

Provare ad aggiungere nel "main" (in fondo) queste linee:

  System.out.println("assegno cuboide c1");
  c2 = c1; // ok
  System.out.println("assegno rettangolo r2 convertito a cuboide");
  c2 = (Cuboid)r2; // ok perche' r2 contiene un cuboide
  System.out.println("assegno rettangolo r1 convertito a cuboide");
  c2 = (Cuboid)r1; // errore perche' r1 contiene un rettangolo

Nota: l'interprete Java vi dice esattamente quale errore e' avvenuto e in quale linea di codice.