Definizioni di nuove classi

classe=tipo degli oggetti (es. String, Point, Rectangle)

Da non confondere con la parola class che va messa all'inizio del programma!

tutti gli oggetti di uno stesso tipo hanno in comune:

  1. componenti
  2. metodi

Definire una nuova classe=specificare quali componenti o metodi ha una classe

In questa lezione: definire le componenti di una classe


Progettazione di una classe

Primo: a cosa serve la classe?

Esempio: dati di uno studente:

  1. nome: una stringa
  2. data di nascita: una stringa
  3. media: un intero

Sarebbe comodo avere oggetti cosí:

public static void main() {
  Studente s, q;

  s.nome="Tizio Semproni";
  s.data="12/3/1932";
  s.media="18";

  ...
}

Se si compila questo programma, dà errore (il tipo Studente non esiste).

Utile: quando si deve definire un nuovo tipo di dati, scrivere prima un programma in cui si usa.

Serve a capire come deve essere fatto l'oggetto.


Progettazione: dati

Dal programma di esempio, si capisce che il nuovo tipo dovrebbe essere fatto cosí:

A parole, il nuovo tipo viene specificato cosí:

Crea nuovo tipo di oggetti Studente con:
|  componente String, di nome .nome
|  componente String, di nome .data
|  componente int, di nome .media

Questa definizione a parole si può ottenere guardando il programma di esempio.

Infatti, dal programma si vede che il tipo si chiama Studente, mentre ogni variabile di questo tipo ha queste tre componenti.


Traduzione: specifica a parole -> linguaggio

Questo è quello che voglio dire al calcolatore:

Crea nuovo tipo di oggetti Studente con:
|  componente String, di nome .nome
|  componente String, di nome .data
|  componente int, di nome .media

Non è codice Java.

Nella sintassi del linguaggio, diventa:

class Studente {
  String nome;
  String data;
  int media;
}

Osservazioni

Fare attenzione:


Definizione e uso di una nuova classe, in pratica

La definizione della classe va in un file,
il programma va in una altro.

Sono due file separati

File Studente.java:
class Studente {
  String nome;
  String data;
  int media;
}
    File DBUniv.java:
class DBUniv {
  public static void main(String args[]) {
    Studente x;
    x=new Studente();

    x.nome="Ciccio";
    x.data="12/12/1959";
    x.media=18;

    System.out.println(x.nome);

    x.media=x.media+1;
    System.out.println(x.media);
  }
}

Attenzione!

Non posso fare class Studente in tutti e due i file.


Compilazione

Cosa devo fare?

Le istruzioni da eseguire stanno nel programma.
Quindi, eseguo il programma.

La classe dice solo come sono fatti i dati di un certo tipo.


Dichiarazione di variabile e di componente

Una dichiarazione int x ha significato diverso se sta nel programma o nella classe:

nel programma:
crea una variabile intera di nome x
nella classe:
ogni oggetto o di questo tipo deve avere una componente intera di nome o.x

Nel programma: definisco una variabile; nella classe: dico come saranno fatti internamente gli oggetti.


Memoria, dopo int x

Se fa int x, cosa succede?

nel programma
crea una zona di memoria (rettangolo) in cui si può memorizzare un intero
nella classe
non si crea nessuna zona di memoria; verrà creata una casella .x per ogni oggetto del nuovo tipo.

Dalla classe alla descrizione a parole

Finora: descrizione a parole --> definizione di classe

Ora, il contrario: classe --> descrizione a parole

class Studente {
  String nome;
  String data;
  int media;
}

significa the Studente è un nuovo tipo di dato, e che ogni variabile di questo tipo ha una componente String di nome .nome, una componente String di nome .data e una int di nome .media


Traduzione inversa: esercizio

Dare la descrizione a parole di questo nuovo tipo:

class Abcd {
  int x;
  int y;
}

Soluzione

Facciamo il processo meccanico inverso:

class Abcd {
  int x;
  int y;
}

Con le regole di prima, significa:

Crea nuovo tipo di dato Abcd con:
| componente int di nome x
| componente int di nome y

È semplicemente l'inversione della traduzione di prima.


Altro esercizio sulla stessa classe

Crea nuovo tipo di dato Abcd con:
| componente int di nome x
| componente int di nome y

Sulla base di questa descrizione, scrivere un programma di esempio.

Ossia, voglio un programma che usa questa classe


Soluzione

Dato che Abcd è il nome della classe, si possono definire variabili di quel tipo, e creare:

class Prova {
  public static void main(String args[]) {
    Abcd a, b;
    a=new Abcd();
    b=new Abcd();

    ...

Attenzione! Questo è il programma (l'altro era il file di definizione del tipo).


Soluzione: uso delle variabili

Dato che Abcd ha componenti x e y, sono definite le variabili intere a.x, a.y, b.x e b.y

class Prova {
  public static void main(String args[]) {
    Abcd a, b;
    a=new Abcd();
    b=new Abcd();

    a.x=12;
    a.y=3;

    b.x=4;
    b.y=-1;

    System.out.println(a.x);
  }
}

Osservazione

Quando faccio int x nella classe, non è una dichiarazione di variabile.

Se non creo nessun oggetto Abcd, allora non viene creata nessuna variabile .x, se creo cento oggetti creo cento variabili .x, ecc.

Ho due variabili in questo programma, ma potevo usarne uno, tre, cento, ecc.
Non c'entra con il fatto che questi oggetti hanno due componenti.


Metodi della classe

Abbiamo visto solo come definire classi senza metodi.

Anche se sono definite la componenti x e y, non è definito il metodo distance, ecc.

Vedremo poi come si fanno a definire i metodi per le nuove classi.


Esercizio: progettare e usare un nuovo tipo

Ogni oggetto contiene i dati di un film: titolo, regista, anno di uscita e durata del film.

Seguire la solita procedura:

  1. dare una definizione a parole dei nuovi dati
  2. scrivere un programma di esempio
  3. tradurre la definizione nel linguaggio Java
  4. compilare ed eseguire

Film: definizione a parole

So quali sono i dati:

crea un nuovo tipo di dati Film con:
| componente String di nome titolo
| componente String di nome regista
| componente int di nome anno
| componente int di nome durata

Film: programma di esempio

Un qualsiasi programma che usa variabili di tipo Film va bene, per ora:

class ProvaFilm {
  public static void main(String args[]) {
    Film f;
    f=new Film();

    f.titolo="Ombre rosse";
    f.regista="Ford";
    f.anno=1958;
    f.durata=120;

    int x;
    x=f.anno-2;

    System.out.println(f.titolo);
  }
}

Nota: l'istruzione System.out.println(f) non stampa tutte le componenti di f


Film: la classe

Si tratta di tradurre la descrizione a parole:

class Film {
  String regista;
  String titolo;
  int anno;
  int durata;
}

Esercizio: tipo corso

Specifiche del tipo:

  1. nome del tipo: Corso
  2. dati da rappresentare:
    1. nome del corso
    2. nome del docente
    3. anno in cui si tiene
    4. numero di ore

Da questa definizione è possibile definire il tipo in modo univoco!


Primo passo: definizione informale

In base quanto detto sopra:

crea un nuovo tipo Corso con:
| componente String di nome .titolo
| componente String di nome .docente
| componente int di nome .anno
| componente int di nome .ore

Secondo passo: un programma di esempio

Questo serve per due motivi:

  1. verificare se la definizione è corretta
    (ossia, se poi il tipo si può usare come ci aspettiamo)
  2. fare il test del file che definisce la classe
    (nella classe non ci sono istruzioni: per fare la verifica serve un programma che usa la classe)

Un esempio di possibile programma:

class Ing {
  public static void main(String args[]) {
    Corso fondam;
    fondam=new Corso();

    fondam.titolo="Fondamenti di Informatica";
    fondam.docente="Paolo Liberatore";
    fondam.anno=2002;
    fondam.ore=100;

    // aggiornare al nuovo anno
    fondam.anno=fondam.anno+1;

    System.out.println(fondam.titolo);
    System.out.println(fondam.docente);
    System.out.println(fondam.ore);
  }
}

Il nome del file in cui questo va messo è Ing.java

Il nome del file deve coincidere con quello che è scritto dopo class

Qualsiasi altro programma che definiva una o più variabili del nuovo tipo, e poi le usava in qualche modo, andava bene!


Terzo passo: definire le componenti

Si tratta solo di tradurre la definizione in Java:

crea un nuovo tipo Corso contenente:
| componente String di nome .titolo
| componente String di nome .docente
| componente int di nome .anno
| componente int di nome .ore

Solito metodo di traduzione:

class Corso {
  String titolo;
  String docente;
  int anno;
  int ore;
}

Lo scriviamo su un file Corso.java


Compilare ed eseguire

Tutti e due i file vanno compilati:

  javac Corso.java
  javac Ing.java

Il primo file dice solo come sono fatti gli oggetti di tipo Corso, mentre il secondo è il programma. Quindi solo il secondo va eseguito:

  java Ing

Questo fa capire a cosa serve il programma di esempio: con Corso.java da solo non potevo eseguire nessun programma.

Con il programma di esempio, ho un programma da eseguire per vedere se la definizione di tipo va bene.


Esercizio: nuovo tipo per i triangoli

Definire una classe per rappresentare triangoli.

Non servono altre specifiche.


Primo passo: definizione informale

Ogni oggetto è un triangolo

Nome della classe: Triangolo

Ogni triangolo è rappresentato da tre punti.

Quindi, ho tre componenti di tipo Point

Definizione del tipo:

crea nuovo tipo Triangolo con:
| componente tipo Point di nome .primo
| componente tipo Point di nome .secondo
| componente tipo Point di nome .terzo

Secondo passo: scrivere il programma di esempio

Basta che sia un qualsiasi programma che fa qualcosa su oggetti del tipo.

Per esempio, definire due triangoli, calcolare il perimetro del primo e l'area del secondo.

import java.awt.*;

class PerArea {
  public static void main(String args[]) {
    Triangolo a;
    a=new Triangolo();

    Triangolo b;
    b=new Triangolo();

    a.primo=new Point();
    a.primo.x=10;
    a.primo.y=20;

    a.secondo=new Point();
    a.secondo.move(20,30);

    a.terzo=new Point();
    a.terzo.move(15,20);

    int dx=a.primo.x - a.secondo.x;
    int dy=a.primo.y - a.secondo.x;

    double lato1;
    lato1=Math.sqrt(dx*dx+dy*dy);

    System.out.println(lato1);
  }
}

Questo programma contiene solo il calcolo di un lato: il perimentro e l'area si calcolano nel solito modo.

Prima vediamo la definizione di tipo e poi facciamo commenti su questo programma.


Terzo passo: traduzione della classe

La specifica informale era:

crea nuovo tipo Triangolo con:
| componente Point di nome .primo
| componente Point di nome .secondo
| componente Point di nome .terzo

La traduzione avviene nel modo ovvio.

import java.awt.*;

class Triangolo {
  Point primo;
  Point secondo;
  Point terzo;
}

Importante: come usare gli oggetti

Regola generale:

Se og è una variabile di un tipo oggetto, e
.comp è una sua componente,
allora og.comp è una variabile.

Se og.comp è una variabile oggetto,
valgono tutte le regole degli oggetti

Nell'esempio, a.primo è una variabile di tipo Point:
vale tutto quello che vale per le variabili Point


Oggetti come componenti

Dato che a.primo è una variabile di tipo Point

  1. prima di usare l'oggetto a.primo
    lo devo creare

  2. a.primo.x e a.primo.y sono variabili intere

  3. posso invocare il metodo a.primo.move(10,20)

Per quello che riguarda il punto 3, ora vediamo l'evoluzione della memoria.


Definizione variabile Triangolo

    Triangolo a;

Produce il seguente stato della memoria:

Esiste solo la variabile, ma non l'oggetto.

Non esistono le variabili a.primo ecc.


Creazione dell'oggetto Triangolo

  a=new Triangolo();

La new crea effettivamente il nuovo oggetto:

Esistono le variabili a.primo ecc.

Importante: non esistono (ancora) gli oggetti punto!

Quindi, a.primo.x, a.primo.y, ecc. non esistono ancora

Fare a.primo.x=10; oppure a.primo.move(10,20) a questo punto dà un errore
(prima di poter usare un oggetto, lo devo creare)


Creazione del punto

    a.primo=new Point();

Questo crea effettivamente il punto.

Solo ora l'oggetto a.primo si può usare.

Ora si può fare a.primo.x=10 oppure a.primo.move(10,20)


Copiatura degli oggetti

Copiare= creare una nuova zona di memoria+
copiarci i dati della zona di memoria originaria.

Dopo aver fatto:

  Point p;
  p=new Point();
  p.move(10,20);

Questo codice fa la copia

  a.primo=new Point();
  a.primo.move(p.x, p.y);

Questo invece indica lo stesso oggetto:

  a.primo=p;

Stato della memoria

Prima:

Dopo aver fatto a.primo=p

Si tratta sempre dello stesso punto!

Se faccio p.x=12, viene modificato anche a.primo.x

Se copio con a.primo=new Point(); a.primo.move(p.x, p.y);:

La copia crea un nuovo oggetto punto


La new nella classe

Non si può fare una cosa del genere:

// Codice errato
import java.awt.*;

class Triangolo {
  Point primo;
  primo=new Primo();

  Point secondo;
  secondo=new Secondo();

  Point terzo;
  terzo=new Terzo();
}

È una regola del linguaggio.

È possibile creare oggetti in una classe, ma soltanto nella definizione di un metodo.

In altre parole: si possono creare oggetti, ma non in questo modo.