Lezione 3 - Exception, Thread e ArrayList :tre oggetti molto speciali


Le prime 2 applicazioni in questa lezione sono state prese dai materiali del seminario tenuto al Cern da Raúl Ramos-Pollán.Allo stesso indirizzo al Cern e' disponibile una copia zippata dello stesso materiale, mentre la S accanto al titolo di ogni applicazione punta a una copia locale dello stesso materiale.
Gli oggetti Exception e Thread hanno un uso particolare in Java:il primo assicura la trattazione degli errori run-time,il secondo la multiprogrammazione. Anche se magari a noi le due cose non interessano, prima o poi ci ritroveremo davanti a queste due tecniche che in Java hanno un uso molto esteso. Ad esempio e' esperienza comune che il compilatore si rifiuti di compilare un pezzo di codice perche' non avete trattato gli errori.O ancora per avere un'applicazione grafica che risponda subito e non si blocchi siete costretti a usare la multiprogrammazione.
A concludere la lezione ci sono alcuni programmi che mostrano come si realizzano in Java le strutture di dati e i contenitori di oggetti. Per far questo useremo l'oggetto ArrayList che e' anch'esso un contenitore di oggetti(negli esercizi useremo anche l'oggetto Vector che e' un contenitore ora deprecato).

Trattamento degli errori in Java

Cominciamo dalla trattazione degli errori e dall'oggetto Exception. Perche' la trattazione degli errori in Java e' cosi' complicata? Nei linguaggi precedenti (come il C o il Fortran) la trattazione degli errori era lasciata alla buona volonta' del programmatore e percio' quasi sempre assente. Java ,essendo dal principio nato per missioni "critiche",ha una trattazione degli errori sofisticata. Lo scopo di tutto questo e' impedire che il vostro programma possa far andare in tilt il computer.
Come ogni cosa in Java tutto viene fatto a partire da un oggetto Exception.Tutti i metodi che possono causare problemi devono essere dichiarati con la definizione throws Exception. Il metodo che ha provocato l'errore deve creare e "lanciare" un'eccezione con l'istruzione throw. Quando un altro oggetto tenta di usare quel metodo il compilatore non accetta il codice a meno che non contenga un particolare costrutto sintattico contenente le operazioni da fare nel caso il problema si verifichi.Si dice che l'oggetto acchiappa l'eccezione (catch). Comunque se non volete gestire l'eccezione potete sempre rilanciarla all'oggetto chiamante aggiungendo throws nomeeccezione alla dichiarazione del metodo.Un gestore default di eccezioni provvedera' ad elaborarla per voi.


Applicazione 1:Creare una classe contatore che segnala un errore quando il valore del contatore supera un valore massimo. S

    file Counter.java

public class Counter {
        int value = 0;
        int topValue = 5;
        public int increment() throws Exception {
                if (value >= topValue) 
                        throw (new Exception());
                value ++;
                return value;
        }
        public int getValue() {
                return value;
        }
}

    file MyApplication.java

public class MyApplication {

    public static void main (String args[]) {

        Counter c1 = new Counter();

        System.out.println("Counter 1 has value "+c1.getValue());

        try {
           for (int i=1; i<10; i++) {
               c1.increment();
               System.out.println("Counter 1 is now "+c1.getValue());
           }
        } catch (Exception e) {
           System.out.println("I just caught the Exception");
        }

    }
}

Abbiamo visto un metodo lanciare un'eccezione e il programma chiamante trattare la stessa con:
try {istruzioni che possono causare errore
           }
        } catch (Exception e) { istruzioni da eseguire se si verifica l'errore
           );
        }




Applicazione 2:Rendere piu' intelligibile la condizione di errore creando una nuova classe CounterException che eredita la classe Exception.S

    file CounterException.java

public class CounterException extends Exception {
                String message;
                public CounterException (String msg) {
                        message = msg;
                }
                public String getErrorMessage() {
                        return message;
                }
}

    file Counter.java

public class Counter {
        int value = 0;
        int topValue = 5;
        public int increment() throws CounterException {
                if (value >= topValue) 
                        throw (new CounterException("Counter Exceeded"));
                value ++;
                return value;
        }
        public int getValue() {
                return value;
        }
}


    file MyApplication.java

public class MyApplication {

    public static void main (String args[]) {

        Counter c1 = new Counter();

        System.out.println("Counter 1 has value "+c1.getValue());

        try {
           for (int i=1; i<10; i++) {
               c1.increment();
               System.out.println("Counter 1 is now "+c1.getValue());
           }
        } catch (CounterException e) {
           System.out.println(e.getErrorMessage());
        }

    }
}

In definitiva in Java la trattazione degli errori si fa estendendo i metodi tradizionali che oltre ad avere certi parametri e un valore di ritorno adesso lanciano anche un certo numero di eccezioni.Nella documentazione standard trovate una lunga lista di oggetti sottoclassi di Exception che vengono usati per le tutte le cause di errori dei metodi delle librerie del Jdk.

La Multiprogrammazione in Java

Passiamo ora alla multiprogrammazione cioe' alla possibilita' di far partire dei processi che girano in parallelo col processo che li ha fatti partire.Il tutto viene gestito attraverso l'oggetto Thread.Il processo da eseguire va indicato nel metodo run dello stesso oggetto, mentre i metodi start e stop possono essere usati per far partire/stoppare il processo in parallelo. Il processo si ferma anche perche' l'algoritmo indicato in run ha finito l'esecuzione. Il metodo stop e' stato deprecato e negli esempi che seguono si fa fermare il Thread usando una tecnica diversa.
La multiprogrammazione con Thread in un applet e' un'interessante caso di uso di oggetto del quale abbiamo bisogno di ridefinire un metodo di una classe diversa dalla classe madre :in questo caso il metodo run di Thread mentre la classe madre e' Applet.


Applet 3 : realizzazione di un orologio digitale con la multiprogrammazione. S
import java.awt.*;
import java.applet.*;
import java.lang.*;
import java.util.Date;

 public class Clock  extends Applet 
 implements Runnable {
   Font f1 = new Font("Serif",Font.BOLD, 24);
   Date d;
   Thread a;

   public void start() {
    if (a == null) {
        a = new Thread(this);
        a.start();
    }
   }

  public void stop() {
     if (a != null) {
       a = null;
     }
   }

   public void run() {
     while(a != null) {
       d = new Date();
       repaint();
       try { Thread.sleep(1000);}
       catch (InterruptedException e) { }
  }
 }
     public void  paint(Graphics g) {
         g.setFont(f1);
         g.drawString(d.toString(),10,50);
    }
 }
Vedete qui l'applet in funzione. La prima soluzione implementata consiste nell'usare l'interfaccia Runnable.


Applet 4 : come il precedente ma ora ridefiniamo l'oggetto Thread. S
import java.awt.*;
import java.applet.*;
import java.lang.*;
import java.util.Date;

 public class actor1  extends Applet  {
   Font f1 = new Font("Serif",Font.BOLD, 24);
   Date d;
   MyThread a;

   public void start() {
    if (a == null) {
        a = new MyThread(this);
        a.start();
    }
   }

  public void stop() {
     if (a != null) {
       a = null;
     }
   }

     public void  paint(Graphics g) {
         g.setFont(f1);
         g.drawString(d.toString(),10,50);
    }
 }
class MyThread extends Thread{
   actor1 act;
   public MyThread(actor1 act1){
     super();
     act = act1;
     }
   public void run() {
     while(true) {
       act.d = new Date();
       act.repaint();
       try { Thread.sleep(1000);}
       catch (InterruptedException e) { }
  }
 }
}
Vedete qui l'applet in funzione.
Nella seconda soluzione abbiamo invece creato un oggetto che estende Thread. Dato che in questo oggetto dovevamo accedere alle proprieta' e i metodi dell'applet, abbiamo ridefinito il costruttore in modo da caricare l'indirizzo dell'applet.


Applet 5 : realizzazione di un'animazione senza multiprogrammazione. S
import java.applet.*;
import java.awt.*;

  public class Anim0 extends Applet  {

   int x,y,r;
   
   public void init() {
     x = 0; y=200; r=50;

    while(true) {
      for (x = r; x <= size().width ; x=x+1) {
        repaint();
 }
 }
 }
    public void  paint(Graphics g) {
      g.setColor(Color.red);
      g.fillOval(x,y,2*r,2*r);
   }
}
Vedete qui l'applet in funzione.
Non si vede niente,perche' non abbiamo alcun controllo sul tempo.Quando riusciamo a guardare l'applet, l'animazione e' gia' finita.


Applet 6 : realizzazione di un'animazione con la multiprogrammazione. S
 import java.applet.*;
import java.awt.*;

  public class Anim1 extends Applet implements Runnable {

   Thread animazione;
   int x,y,r;
   
   public void init() {
     x = 0; y=200; r=50;
       }

   public void start() {
    if (animazione == null) {
        animazione = new Thread(this);
        animazione.start();
    }
   }

  public void stop() {
     if (animazione != null) {
       animazione = null;
     }
   }

   public void run() {
    while(animazione != null) {
      for (x = r; x <= size().width ; x=x+1) {
        repaint();
        try { Thread.sleep(100);}
       catch (InterruptedException e) { }
 }
 }
 }
    public void  paint(Graphics g) {
      g.setColor(Color.red);
      g.fillOval(x,y,2*r,2*r);
      System.out.println("x,y,z="+x+" "+y+" "+r);
   }
} 
Vedete qui l'applet in funzione.



Applet 7 : come il precedente ma con 2 "trucchi" per migliorare l'animazione. S
import java.applet.*;
import java.awt.*;

  public class Anim2 extends Applet implements Runnable {

   Thread animazione;
   int x,y,r;
   Image o;
   Graphics og;
   
   public void init() {
     x = 0; y=200; r=50;
     o = createImage(size().width,size().height);
     og = o.getGraphics();
     }

   public void start() {
    if (animazione == null) {
        animazione = new Thread(this);
        animazione.start();
    }
   }

  public void stop() {
     if (animazione != null) {
       animazione = null;
     }
   }

   public void run() {
    while(animazione!=null) {
      for (x = 0; x <= size().width ; x=x+1) {
        repaint();
        try { Thread.sleep(100);}
       catch (InterruptedException e) { }
 }
 }
 }
      public void update(Graphics g) {
      paint(g);
 }
    public void  paint(Graphics g) {
      og.setColor(Color.white);
      og.fillRect(0,0,size().width,size().height);
      og.setColor(Color.red);
      og.fillOval(x,y,2*r,2*r);
      g.drawImage(o, 0, 0 ,this);
      System.out.println("x,y,z="+x+" "+y+" "+r);
   }
}
Vedete qui l'applet in funzione. In questo caso sono stati applicati due "trucchi" che migliorano l'animazione: Per costruire l'immagine in memoria e' necessario anche un'oggetto Graphics collegato all'immagine. In pratica una volta creati questi oggetti con le istruzioni:
     o = createImage(size().width,size().height);
     og = o.getGraphics();
possiamo eseguire il disegno in memoria usando og invece di g. Quando il disegno e' pronto lo trasferiamo con un sol colpo nella finestra dell'applet con:
g.drawImage(o, 0, 0 ,this);
Da notare che ora dobbiamo procedere noi a cancellare l'immagine precedente con l'istruzione:
      og.setColor(Color.white);
      og.fillRect(0,0,size().width,size().height);
L'uso della multiprogrammazione crea il problema della sincronizzazione tra diversi processi in competizione per la stessa risorsa. Questo succede anche nell'applet precedente dove l'oggetto Image deve essere condiviso da 2 processi:quello che richiama il metodo paint e il processo che fa andare l'animazione.Percio' l'accesso all'immagine deve essere controllato. Questo lo si fa dichiarando un intero metodo o anche una singola istruzione synchronized.


Applet 8 : come il precedente ma sincronizzando i processi di paint e di animazione. S
import java.applet.*;
import java.awt.*;

  public class Anim4 extends Applet implements Runnable {

   Thread animazione;
   int x,y,r;
   Image o;
   Graphics og;
   
   public void init() {
     x = 0; y=200; r=50;
     }

   public void start() {
    if (animazione == null) {
        animazione = new Thread(this);
        animazione.start();
    }
   }

  public void stop() {
     if (animazione != null) {
       animazione = null;
     }
   }

   public void run() {
    while(animazione != null) {
      for (x = 0; x <= getSize().width ; x=x+1) {
        createNextFrame();
        repaint();
        synchronized(this){
        try { wait(100);}
       catch (InterruptedException e) { }
        }
 }
 }
 }
      synchronized void createNextFrame(){
     if(o==null){
       o = createImage(getSize().width,getSize().height);
       og = o.getGraphics();
        }
      og.setColor(Color.white);
      og.fillRect(0,0,getSize().width,getSize().height);
      og.setColor(Color.red);
      og.fillOval(x,y,2*r,2*r);
 }
      public void update(Graphics g) {
      paint(g);
 }
    synchronized public void  paint(Graphics g) {
      g.drawImage(o, 0, 0 ,this);
   }
}
Vedete qui l'applet in funzione. Dichiarando synchronized i due metodi paint e nextFrame che accedono alla stessa risorsa(l'immagine offline) si evita che possano intralciarsi. Infatti i due metodi sono eseguiti in due diversi Thread e potrebbe accadere che la paint aggiorni l'immagine mentre la nextFrame sta aggiornando la stessa.Solo uno dei due metodi puo' essere in esecuzione in un dato momento.
In pratica in Java ogni oggetto ha un lock(catenaccio) che puo' essere bloccato da un Thread ma solo un Thread puo' bloccare il "lock" in un dato momento. Per eseguire un metodo sincronizzato, un Thread deve ottenere il controllo del lock.Se in quel momento un altro Thread controlla il lock, allora deve aspettare che l'altro abbia finito. In questo modo, ponendo l'accesso di risorse condivise solo in metodi sincronizzati, si garantisce a ogni Thread che accede alla risorsa un accesso esclusivo alla stessa.
Qui vediamo anche l'uso di wait un metodo che puo' essere chiamato solo in un pezzo di codice sincronizzato:cioe' quando il Thread e' in possesso del lock. Dopo l'esecuzione di wait il lock viene rilasciato e il Thread aspetta che passi il tempo indicato (in millisecondi) prima di riprendere l'esecuzione, oppure che un altro Thread chiami il comando di notify() per dire che la risorsa e' disponibile. wait e notify sono metodi di Object e possono essere usati percio' ovunque.
Da notare la possibilita' di sincronizzare la singola istruzione sul lock di un oggetto con synchronized(this){statements}.


Applet 9 : convertite l'applet 6 nella versione Java2 S

import java.awt.*;
import javax.swing.*;



  public class Anim1 extends JApplet implements Runnable {
   
   Display disegno;
   Thread animazione;
   int x,y,r;
   
   public void init() {

     disegno = new Display();
     setContentPane(disegno);
     x = 0; y=200; r=50;
       }

   public void start() {
    if (animazione == null) {
        animazione = new Thread(this);
        animazione.start();
    }
   }

  public void stop() {
     if (animazione != null) {
       animazione = null;
     }
   }

   public void run() {
    while(true) {
      for (x = r; x <= disegno.getSize().width ; x=x+1) {
        disegno.repaint();
        try { Thread.sleep(100);}
       catch (InterruptedException e) { }
 }
 }
 }
   class Display extends JPanel{

    public void  paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(Color.red);
      g.fillOval(x,y,2*r,2*r);
   }
}
}
Vedete qui l'applet in funzione.

In Java 2 c'e' una maniera semplificata di fare animazioni che non ha bisogno di multiprogrammazione. C'e' un oggetto Timer che genera eventi a intervalli prestabiliti. (Parleremo di eventi nel prossimo seminario: ma in pratica e' come se un utente pigiasse un tasto a intervalli prestabiliti).Invece della run ora abbiamo la actionPerformed che aggiorna l'immagine eseguendo l'animazione. Per poterla definire e' necessario implementare l'interfaccia ActionListener.
Applet 10 : realizzate l'animazione con l'oggetto Timer di Swing S

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;


  public class Anim2 extends JApplet implements ActionListener {
   Display disegno;
   Timer timer;
   int x,y,r;
   Image o;
   Graphics og;
   
   public void init() {

     disegno = new Display();
     setContentPane(disegno);
     x = 0; y=200; r=50;
     }

   public void start() {
    if (timer == null) {
        timer = new Timer(100,this);
        timer.start();
    }
   }

  public void stop() {
     if (timer != null) {
       timer.stop();
     }
   }

   public void actionPerformed(ActionEvent e) {
      x = x + 1;
      if(x>disegno.getSize().width)x = 0;
        disegno.repaint();
 }
    class Display extends JPanel{


    public void  paintComponent(Graphics g) {
      super.paintComponent(g);
      g.setColor(Color.red);
      g.fillOval(x,y,2*r,2*r);


   }
   }
}
Vedete qui l'applet in funzione.

Realizzazione di contenitori in Java

In questi ultimi programmi vedremo come realizzare prima una struttura di dati di interi, poi una struttura di dati generica che puo' contenere qualsiasi tipo di oggetto.


Applet 11 :Organizzate 10 istanze di uno stesso oggetto in una struttura a lista.L'applet deve creare la lista caricando nei nodi i numeri da 0 a 9 e quindi ripercorrere la lista stampando il contenuto della stessa . S
import java.applet.Applet;
import java.awt.Graphics;

public class Lista extends Applet{
                ListNode inizio = new ListNode(0);
        public void init() {
                ListNode prossimo = inizio;
                for (int i = 1; i <= 10; i++) {
                        prossimo.next = new ListNode(i);
                        prossimo = prossimo.next;
                }       
        }
        
        public void paint( Graphics g) {
        
                ListNode prossimo=inizio;
                g.drawString("Contenuto della lista: ", 5, 20);

                for (int i = 0; i <= 10; i++) {
                        g.drawString(" "+prossimo.data, 50, i*10+40);
                        prossimo = prossimo.next;
                }
        }
}


class ListNode {
        public ListNode next = null;
        public int data;
        
        public ListNode(int data) {
                this.data = data;
        }
}
Guardate l'applet in funzione.
Questo esempio non sfruttava in pieno le possibilita' di un linguaggio ad oggetti come Java riguardo la creazione di strutture di dati.In effetti Java permette di creare strutture non solo tra oggetti dello stesso tipo ma tra oggetti di tipo diverso. Queste si chiamano di solito Contenitori. Inoltre esso ci permette di definire il comportamento di questo contenitore in maniera astratta,senza dover entrare nei dettagli dell'implementazione. E' quello che vedremo nelle 2 applicazioni che seguono dove definiremo un contenitore di tipo Stack. Le librerie di Java contengono diversi contenitori gia' pronti come Vector e Hashtable.Con questi oggetti, viene usata spesso una classe Enumeration (detta in gergo iteratore) che permette di visitare gli elementi di un contenitore indipendentemente dal tipo di contenitore.
Con la versione 1.2 di Java e' stata creata tutta una serie di nuovi contenitori che hanno in pratica rimpiazzato i vecchi contenitori. Per cui ora al posto di Vector si usa ArrayList ,Hastable e' diventata HashMap e HashSet e l'iteratore invece di Enumeration si chiama Iterator (Confusi !?)


Applicazione 12 :realizzate e testate una struttura di dati di tipo Stack che puo' contenere oggetti di ogni tipo.Definite il comportamento dello stack in un'interfaccia Stack in modo da poter cambiare a piacere l'implementazione .Fate una prima implementazione con un oggetto array S

    file Stack.java

public interface Stack{
   void push(Object o)
     throws StackException;
   Object pop()
     throws StackException;
   Object top()
     throws StackException;
   int size();
 }


    file Stack1.java

public class Stack1 implements Stack{
  int max;
  int size;
  Object[] dati;
public Stack1(int dim){
  dati = new Object[dim];
  max = dim;
  size = 0;
}
public void push(Object o)
 throws StackException 
{
  if(size == max)
    throw new StackException("massimo dello stack superato");
  dati[size++] = o;
 
 }

public Object pop()
 throws StackException 
{
 if(size <=0) 
  throw new StackException("stack vuoto");
 return dati[--size];
}

public Object top()
 throws StackException 
{
 if(size <=0) 
  throw new StackException("stack vuoto");
 return dati[size-1];
}

public int size(){return this.size;}
}

    file StackException.java

public class StackException extends Exception{
   StackException(String msg){super(msg);}
}
Ecco un altro modo di ridefinire un'eccezione in modo da personalizzare il messaggio che da(confronta con l'Applicazione 2).

    file TestStack.java

class TestStack{
    public static void main(String[] args){
     Stack1 s = new Stack1(5);
     try {
      System.out.println("Dimensioni iniziali ="+s.size());
      s.push("uno");
      s.push(new Integer(2));
      s.push(new Double(3.));
      s.push("quattro");
      s.push("cinque");
      s.push("sei");
     }
     catch(StackException e){
      System.out.println(e);}
     try {
      System.out.println("Oggetto top = "+s.top());
      String s1 = (String)s.pop();
      String s2 = (String)s.pop();
      System.out.println(s1+" "+s2);
      int n = s.size();
      for(int i=0;i < n;i++){
      System.out.println("Oggetto "+i+" = "+s.pop());
      }
      s.pop();
     }
     catch(StackException e){
      System.out.println(e);}
}
}

     Output

Dimensioni iniziali =0
StackException: massimo dello stack superato
Oggetto top = cinque
cinque quattro
Oggetto 0 = 3.0
Oggetto 1 = 2
Oggetto 2 = uno
StackException: stack vuoto



Applicazione 13 Come il precedente ma ora fate un'implementazione con l'oggetto Vector S

    file Stack2.java

import java.util.*;
public class Stack2 implements Stack{
   Vector dati;
public Stack2(){
  dati = new Vector();
}
public void push(Object o)
{
  dati.addElement(o);
 
 }

public Object pop()
 throws StackException 
{
 if(dati.size() ==0) 
  throw new StackException("stack vuoto");
  int i = dati.size()-1;
  Object o = dati.elementAt(i);
  dati.removeElementAt(i);
 return o;
}

public Object top()
 throws StackException 
{
 if(dati.size() ==0) 
  throw new StackException("stack vuoto");
 return dati.elementAt(dati.size()-1);
}

public int size(){return dati.size();}
}




Applicazione 14 Come il precedente ma ora fate un'implementazione con l'oggetto ArrayList S

    file Stack2.java


import java.util.*;
public class Stack2 implements Stack{
   ArrayList dati;
public Stack2(){
  dati = new ArrayList();
}
public void push(Object o)
{
  dati.add(o);
 
 }

public Object pop()
 throws StackException 
{
 if(dati.size() ==0) 
  throw new StackException("stack vuoto");
  int i = dati.size()-1;
  Object o = dati.get(i);
  dati.remove(i);
 return o;
}

public Object top()
 throws StackException 
{
 if(dati.size() ==0) 
  throw new StackException("stack vuoto");
 return dati.get(dati.size()-1);
}

public int size(){return dati.size();}
}



Applicazione 15 Usate l'oggetto Vector per caricare una serie di oggetti, quindi l'oggetto Enumeration per stamparne il contenuto. Ripetete la stessa cosa con una ArrayList e un Iterator. S

    file TestEnum.java


 import java.util.*;
 class TestEnum{
    public static void main(String[] args){
     Vector v = new Vector();
      v.addElement("uno");
      v.addElement(new Integer(2));
      v.addElement(new Double(3.));
      v.addElement("quattro");
      v.addElement("cinque");
      v.addElement("sei");
      System.out.println("Dimensioni del contenitore ="+v.size());
      for (Enumeration e = v.elements() ; e.hasMoreElements() ;) {
              System.out.println(e.nextElement());

          }
}
}

    file TestEnum1.java


 import java.util.*;
 class TestEnum1{
    public static void main(String[] args){
     ArrayList v = new ArrayList();
      v.add("uno");
      v.add(new Integer(2));
      v.add(new Double(3.));
      v.add("quattro");
      v.add("cinque");
      v.add("sei");
      System.out.println("Dimensioni del contenitore ="+v.size());
      for (Iterator iter = v.iterator() ; iter.hasNext() ;) {
              System.out.println(iter.next());

          }
}
}
Tutti i contenitori e gli iteratori di Java sono in java.util che dalla versione 2 di Java contiene molte piu' classi di questo tipo. Sono cambiati anche i nomi delle classi. Ad esempio l'iteratore ora non e' piu' realizzato da Enumeration ma da Iterator.
I contenitori sono ora suddivisi in due categorie :collections e maps. Ora l'implementazione di questi contenitori e' molto piu' simile a quella fatta in C++ nella STL(Standard Template Library):e' infatti proprio questa la ragione del cambiamento.Questi oggetti permettono di effettuare in Java la cosiddetta programmazione generica in quanto trattandosi di contenitori di Object permettono di scrivere algoritmi generici che funzionano per qualsiasi tipo di oggetto. Alle collezioni sono inoltre associati dei particolari oggetti iterator che permettono di attraversare la collezione attraverso i metodi next() e hasNext().
Anche il C++ ha la programmazione generica con contenitori e iteratori, ma la sua implementazione, non avendo il C++ una classe Object madre di tutte le classi, e' completamente diversa.
Qui la versione precedente di questa lezione
INDIETRO a Imparate Java in una settimana
INDIETRO a Seminario su Java
Maintained by : info@zitogiuseppe.com
Ultimo aggiornamento: