3. [TD]: Classi
Parole chiave: classe, interfaccia, ereditarietà, eccezione, polimorfismo
Letture consigliate:
- sezioni 2.1, 2.2, 2.4 e 2.7 del Capitolo 2 di [rif1]: Classi e Interfacce
- sezioni 3.3 (classe String), 3.5 (classe ArrayList), 3.6 (classe Arrays)
Nella Parte 1 dell'esercizio ELECTIONS non sono state utilizzate classi. Abbiamo costruito una soluzione come l'avremmo costruita in linguaggio C. Introdurremo ora il concetto di classi Java.
3.1. Supporto
![]() | ![]() |
La cartella [support / chap-03] contiene il progetto Eclipse relativo a questo capitolo.
Ora lavoreremo con JDK 1.8 poiché alcuni dei prossimi progetti richiedono questa versione di JDK. Per determinare quale JDK viene utilizzato, procedere come segue:
![]() |
![]() |
- in [4], il JRE (Java Runtime Environment) in uso. In questo caso, il JRE è in realtà un JDK (Java Development Kit), nello specifico [jdk1.8.0_60]. Se non si tratta di un JDK o se si dispone di una versione inferiore alla 1.8, procedere come segue [5-21];
![]() |
- in [8], il JRE attualmente utilizzato di default da Eclipse;
- in [11], i vari JDK e JRE attualmente riconosciuti da Eclipse;
![]() |
- in [15], scegli un JDK anziché un JRE. Questo documento utilizza progetti Maven che richiedono un JDK;
![]() |
![]() |
- in [21], abbiamo una versione JDK >=1.8;
- in [22-23], si accede ai facet (diverse viste dello stesso progetto Eclipse) del progetto;
![]() |
- in [24], verificare di utilizzare una versione di Java >=1.8;
3.2. La classe [ ListeElectorale]
In C, probabilmente avremmo usato una struttura per rappresentare un elenco di candidati alle elezioni. Avrebbe potuto avere un aspetto simile a questo:
Il concetto di struttura non esiste nel linguaggio Java. Deve essere sostituito da quello di classe. Decidiamo quindi di creare una classe per memorizzare le informazioni relative a un elenco di candidati. Questa classe avrebbe la seguente struttura:
package istia.st.elections;
public class ListeElectorale {
/**
* identité de la liste
*/
private int id;
/**
* nom de la liste
*/
private String nom;
/**
* nombre de voix de la liste
*/
private int voix;
/**
* nombre de sièges de la liste
*/
private int sieges;
/**
* indique si la liste est éliminée ou non
*/
private boolean elimine;
/**
* constructeur par défaut
*/
public ListeElectorale() {
}
/**
*
* @param nom String : le nom de la liste
* @param voix int : son nombre de voix
* @param sieges int : son nombre de sieges
* @param elimine boolean : son état éliminé ou non
*/
public ListeElectorale(int id,String nom, int voix, int sieges, boolean elimine) {
...
}
/**
*
* @return int : l'identifiant de la liste
*/
public int getId() {
...
}
/**
* initialise l'identifiant de liste
* @param id int : identifiant de la liste
* @throws ElectionsException si id<1
*/
public void setId(int id) {
...
}
/**
*
* @return String : le nom de la liste
*/
public String getNom() {
...
}
/**
* initialise le nom de la liste
* @param nom String : nom de la liste
* @throws ElectionsException si le nom est vide ou blanc
*/
public void setNom(String nom) {
...
}
/**
*
* @return int : le nombre de voix de la liste
*/
public int getVoix() {
...
}
/**
* initialise le nombre de voix de la liste
* @param voix int : le nombre de voix de la liste
*/
public void setVoix(int voix) {
...
}
/**
*
* @return int : le nombre de sièges de la liste
*/
public int getSieges() {
...
}
/**
* fixe le nombre de sièges de la liste
* @param sieges int : le nombre de sièges de la liste
*/
public void setSieges(int sieges) {
...
}
/**
*
* @return boolean : valeur du champ elimine
*/
public boolean isElimine() {
...
}
/**
*
* @param sieges int
*/
public void setElimine(boolean elimine) {
...
}
/**
*
* @return String : identité de la liste électorale
*/
public String toString() {
...
}
}
- riga 8: un identificatore univoco per una lista. Non essenziale in questo caso, ma riservato per un uso futuro.
- riga 13: il nome della lista.
- riga 17: il numero di voti per la lista
- riga 21: il numero di seggi per la lista
- riga 25: un valore booleano che indica se la lista è stata eliminata (percentuale di voti ottenuta al di sotto della soglia elettorale) o meno.
Ogni campo privato denominato [xyz] può essere inizializzato utilizzando un metodo denominato [setXyz]. Il metodo [getXyz] recupera il valore del campo privato [xyz]. Nel caso specifico in cui [xyz] sia un campo booleano, il metodo [getXyz] può essere sostituito dal metodo [isXyz]. La denominazione specifica di questi metodi segue uno standard di codifica chiamato standard JavaBean. Pertanto, definiamo i seguenti metodi pubblici:
- getId (riga 48), setId (riga 57)
- getName (riga 65), setName (riga 74)
- getVoice (riga 82), setVoice (riga 90)
- getSeats (riga 98), setSeats (riga 106)
- isEliminated (riga 114), setEliminated (riga 122)
- righe 30-31: definiscono un costruttore senza parametri. Ciò consente di creare un oggetto [VoterList] senza inizializzarlo. Può quindi essere inizializzato utilizzando i metodi set.
- Righe 40–42: definiscono un costruttore che crea un oggetto [VoterList] inizializzandone i cinque campi privati.
- Righe 130–132: definiscono il metodo [toString], che restituisce una stringa contenente i valori dei cinque campi dell’oggetto.
Un programma di test per la classe [VoterList] potrebbe essere simile al seguente:
package istia.st.elections.tests;
import istia.st.elections.ListeElectorale;
public class MainTest1ListeElectorale {
public static void main(String[] args) {
// creation of an electoral list
ListeElectorale listeElectorale1 = new ListeElectorale(1, "A", 32000,
0, false);
// display identity list
System.out.println("listeElectorale1=" + listeElectorale1);
// change in number of seats
listeElectorale1.setSieges(2);
// display identity list 1
System.out.println("listeElectorale1=" + listeElectorale1);
// a new electoral roll
ListeElectorale listeElectorale2 = listeElectorale1;
// display identity list 2
System.out.println("listeElectorale2=" + listeElectorale2);
// change in number of seats
listeElectorale2.setSieges(3);
// display identity of the 2 lists
System.out.println("listeElectorale2=" + listeElectorale2);
System.out.println("listeElectorale1=" + listeElectorale1);
}
}
L'ambiente Eclipse per questo test potrebbe essere il seguente:
![]() |
- [1]: il progetto si chiama [elections-02A]
- [2]: l'applicazione sarà inserita in un pacchetto, in questo caso [istia.st.elections]
- [3]: [VoterList.java] è il codice sorgente della classe [VoterList]
- [4]: Le classi di test saranno collocate in un pacchetto, in questo caso [istia.st.elections.tests]
- [5]: la classe di test [MainTest1VoterList]
La schermata ottenuta dopo l'esecuzione del programma sopra riportato è la seguente:

Compito: utilizzando le informazioni di cui sopra, completare il codice per la classe VoterList.
3.3. Creazione di una classe di eccezione [ElectionsException]
Tra le varie classi di eccezione del linguaggio Java, ce n'è una chiamata [RuntimeException]. Questa classe deriva dalla classe [Exception], la radice di tutte le classi di eccezione. La caratteristica distintiva delle istanze di [RuntimeException] o delle istanze derivate è che non è necessario dichiararle o gestirle. Sono chiamate eccezioni non intercettate.
Vediamo un primo esempio. La classe [BufferedReader] è una classe le cui istanze consentono di leggere righe di testo da un flusso di dati. Dispone di un metodo [readLine] con la seguente firma:
Possiamo notare che il metodo può generare un'eccezione [IOException]. La gerarchia di classe per questa classe è la seguente:
La classe [IOException] deriva dalla classe [Exception] (riga 3). Il compilatore richiede che le eccezioni di tipo [java.lang.Exception] o dei tipi derivati vengano gestite e dichiarate (ad eccezione del ramo [RuntimeException], di cui parleremo più avanti). Pertanto, per leggere una riga di testo digitata sulla tastiera, dobbiamo scrivere qualcosa del tipo:
Prendiamo un altro esempio. Per convertire una stringa in un numero intero, possiamo utilizzare il metodo statico [Integer.parseInt], la cui firma è la seguente:
L'argomento [s] è la stringa da convertire in un numero intero. Possiamo notare che il metodo può generare un'eccezione [NumberFormatException]. La gerarchia di classe per questa classe è la seguente:
La classe [NumberFormatException] estende la classe [RuntimeException] (riga 4). Il compilatore non richiede di gestire o dichiarare le eccezioni di tipo [java.lang.RuntimeException] o delle sue sottoclassi. Pertanto, possiamo scrivere qualcosa del tipo:
Non è necessario includere un blocco [try-catch] per gestire eventuali eccezioni generate da [Integer.parseInt] (riga 9).
La creazione e l'utilizzo di classi di eccezione derivate da [RuntimeException] presentano vantaggi e svantaggi:
- lato positivo: il codice è più conciso
- Tra gli svantaggi: potremmo finire per ricorrere a metodi in stile C in cui ogni funzione restituisce un codice di errore — una pratica che pochi usano, proprio per mantenere il codice leggero. Quando si verifica un errore non gestito di questo tipo, il programma va in crash, solitamente in modo poco elegante.
Abbiamo deciso di creare una classe speciale che raggruppi tutte le eccezioni che potrebbero verificarsi nella nostra applicazione ELECTIONS. Si chiamerà [ElectionsException] e deriverà dalla classe [RuntimeException]. Il suo codice è il seguente:
package istia.st.elections;
public class ElectionsException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ElectionsException() {
super();
}
public ElectionsException(String message) {
super(message);
}
public ElectionsException(Throwable cause) {
super(cause);
}
public ElectionsException(String message, Throwable cause) {
super(message, cause);
}
}
- Riga 1: inseriamo la classe nel pacchetto [istia.st.elections];
- riga 3: la classe estende [RuntimeException]. È quindi non controllata;
- riga 4: un identificatore di serializzazione che per ora possiamo ignorare;
- Nella nostra applicazione useremo due tipi di costruttori:
- quello classico alle righe 15–17, come mostrato di seguito:
In questo caso, il metodo che chiama un metodo che genera tale eccezione può gestirla come segue:
// test exception
try {
listeElectorale2.setSieges(-3);
} catch (ElectionsException ex) {
System.err.println("L'exception suivante s'est produite : ["
+ ex.toString() + "]");
}
- (continua)
- oppure quello alle righe 14–20, progettato per propagare un'eccezione già verificatasi avvolgendo la stessa in un [ElectionsException]:
try {
...;
} catch (SQLException ex) {
// on encapsule l'exception
throw new ElectionsException("erreur de fermeture de la connexion à la BD",ex);
}
Questo secondo metodo ha il vantaggio di conservare le informazioni contenute nella prima eccezione. In questo caso, il metodo che chiama un metodo che genera tale eccezione può gestirla come segue:
try {
...;
} catch (ElectionsException ex) {
System.out.println(ex.getMessage() + ", Cause : "+ ex.getCause().getMessage());
System.exit(1);
}
Compito: Modificare il codice della classe ListeElectorale in modo che i metodi set generino un'eccezione [ElectionsException] se l'inizializzazione richiesta non è corretta, ad esempio se si inizializza il nome con una stringa vuota.
Il progetto di test Eclipse per questa nuova versione potrebbe essere il seguente:
![]() |
- [1]: il progetto si chiama [elections-02B]
- [2]: l'applicazione è collocata in un pacchetto, in questo caso [istia.st.elections]
- [3]: le classi [VoterList] e [ElectionsException]
- [4]: le classi di test si trovano in un pacchetto, in questo caso [istia.st.elections.tests]
- [5]: La classe di test [MainTest1VoterList]
La classe di test [MainTest1VoterList] discussa in precedenza è stata leggermente modificata per testare i casi di eccezione:
package istia.st.elections.tests;
import istia.st.elections.ElectionsException;
import istia.st.elections.ListeElectorale;
public class MainTest1ListeElectorale {
public static void main(String[] args) {
// creation of an electoral list
ListeElectorale listeElectorale1 = new ListeElectorale(1, "A", 32000,
0, false);
// display identity list
System.out.println("listeElectorale1=" + listeElectorale1);
// change in number of seats
listeElectorale1.setSieges(2);
// display identity list 1
System.out.println("listeElectorale1=" + listeElectorale1);
// a new electoral roll
ListeElectorale listeElectorale2 = listeElectorale1;
// display identity list 2
System.out.println("listeElectorale2=" + listeElectorale2);
// change in number of seats
listeElectorale2.setSieges(3);
// display identity of the 2 lists
System.out.println("listeElectorale2=" + listeElectorale2);
System.out.println("listeElectorale1=" + listeElectorale1);
// test exception
try {
listeElectorale2.setSieges(-3);
} catch (ElectionsException ex) {
System.err.println("L'exception suivante s'est produite : ["
+ ex.toString() + "]");
}
}
}
- riga 28: si tenta di inizializzare il numero di posti con un valore non valido
- riga 30: se si verifica un'eccezione, questa viene visualizzata
L'esecuzione del test produce i seguenti risultati:

Si noti che la classe [VoterList] ha effettivamente generato un'eccezione quando abbiamo tentato di inizializzare il numero di seggi con un valore non valido (riga 28 del codice).
3.4. Una classe di test unitario
Il tipo di test precedente si basa sulla verifica visiva. Controlliamo che ciò che viene visualizzato sullo schermo corrisponda a quanto previsto. Questo metodo non è raccomandato in un contesto professionale. I test dovrebbero sempre essere automatizzati il più possibile e mirare a non richiedere alcun intervento umano. Gli esseri umani sono infatti soggetti a stanchezza e la loro capacità di verificare i test diminuisce nel corso della giornata.
Un'applicazione si evolve nel tempo. Ad ogni aggiornamento, dobbiamo verificare che l'applicazione non subisca una "regressione", ovvero che continui a superare i test funzionali eseguiti durante lo sviluppo iniziale. Questi test sono chiamati test di "non regressione". Un'applicazione di dimensioni moderate può richiedere centinaia di test. Infatti, viene testato ogni metodo in ogni classe dell'applicazione. Questi sono chiamati test unitari. Possono impegnare molti sviluppatori se non sono stati automatizzati.
Sono stati sviluppati strumenti per automatizzare i test. Uno di questi si chiama [JUnit]. Si tratta di una libreria di classi progettata per gestire i test. Useremo questo strumento per testare la classe [VoterList].
Un programma di test JUnit (versione 4.x) ha la seguente forma:
package istia.st.elections.tests;
import org.junit.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class JUnitEssai {
@Before
public void avant() throws Exception {
System.out.println("tearUp");
}
@After
public void après() throws Exception {
System.out.println("tearDown");
}
@Test
public void t1() {
System.out.println("test1");
Assert.assertEquals(1, 1);
}
@Test
public void t2() {
System.out.println("test2");
Assert.assertEquals(1, 2);
}
}
- riga 1: la classe è stata inserita nel pacchetto [istia.st.elections.tests];
- riga 11: il metodo annotato con l'annotazione [@Before] viene eseguito prima di ogni test unitario;
- riga 16: il metodo annotato con l'annotazione [@After] viene eseguito dopo ogni test unitario;
- riga 21: un metodo annotato con l'annotazione [@Test] è un metodo testato dal test unitario. I metodi annotati con [@Test] verranno eseguiti uno dopo l'altro, a meno che non sia specificato diversamente dal tester, che può selezionare autonomamente i metodi da testare. Prima di ogni esecuzione di un metodo [@Test], viene eseguito il metodo [@Before]. Dopo ogni esecuzione di un metodo [@Test], viene eseguito il metodo [@After];
- righe 22–25: definiscono un metodo di test [t1];
- Riga 18: uno dei metodi [Assert.assert*] utilizzati per verificare le asserzioni. Sono disponibili i seguenti metodi [assert]:
- assertEquals(espressione1, espressione2): verifica che i valori delle due espressioni siano uguali. Sono accettati molti tipi di espressioni (int, String, float, double, boolean, char, short). Se le due espressioni non sono uguali, viene generata un'eccezione [AssertionFailedError],
- assertEquals(real1, real2, delta): verifica che due numeri reali siano uguali entro un margine delta, ovvero abs(real1-real2) <= delta. Ad esempio, si potrebbe scrivere assertEquals(real1, real2, 1E-6) per verificare che due valori siano uguali entro 10⁻⁶,
- assertEquals(message, expression1, expression2) e assertEquals(message, real1, real2, delta) sono varianti che consentono di specificare il messaggio di errore da associare all'eccezione [AssertionFailedError] generata quando il metodo [assertEquals] fallisce,
- assertNotNull(Object) e assertNotNull(message, Object): verifica che Object non sia uguale a null,
- assertNull(Object) e assertNull(message, Object): verifica che Object sia uguale a null,
- assertSame(Object1, Object2) e assertSame(message, Object1, Object2): verifica che i riferimenti Object1 e Object2 puntino allo stesso oggetto,
- assertNotSame(Object1, Object2) e assertNotSame(message, Object1, Object2): verifica che i riferimenti Object1 e Object2 non puntino allo stesso oggetto;
- riga 24: questa asserzione deve passare;
- riga 30: questa asserzione deve fallire;
Nell'ambiente Eclipse, una classe di test JUnit può essere creata come segue:
![]() |
- [1]: fare clic con il tasto destro del mouse sul pacchetto in cui si desidera aggiungere la classe di test, quindi selezionare [JUnit / Nuovo / Caso di test JUnit]
![]() |
- [1]: Seleziona una versione di JUnit;
- [2]: Seleziona la cartella in cui deve essere creata la classe di test;
- [3]: Seleziona il pacchetto in cui deve essere creata la classe di test;
- [4]: Inserisci il nome della classe di test;
- [5]: Selezionare i metodi da includere nella classe che verrà generata;
- [6]: La classe JUnitEssai è stata generata
La procedura guidata precedente genera una classe quasi vuota:
package istia.st.elections.tests;
import org.junit.Assert;
import org.junit.After;
import org.junit.Before;
public class JUnitEssai {
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
}
Completiamo e modifichiamo il codice precedente come segue:
package istia.st.elections.tests;
import org.junit.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class JUnitEssai2 {
@Before
public void avant() throws Exception {
System.out.println("tearUp");
}
@After
public void après() throws Exception {
System.out.println("tearDown");
}
@Test
public void t1() {
System.out.println("test1");
Assert.assertEquals(1, 1);
}
@Test
public void t2() {
System.out.println("test2");
Assert.assertEquals(1, 2);
}
}
In Eclipse, fare clic con il tasto destro del mouse sulla classe di test, quindi selezionare [Esegui come / Test JUnit] per eseguirla:

I risultati ottenuti eseguendo questo test sono i seguenti:

Come si vede sopra, il metodo [test2] ha dato esito negativo. Ogni volta che un test fallisce, viene visualizzato un messaggio di errore. Per [test2], è quello mostrato sopra. Il messaggio indica il numero di riga in cui si è verificato l'errore (riga 30). Alla riga 30, la chiamata che ha dato esito negativo era:
Assert.assertEquals(1, 2);
Il primo parametro è chiamato valore atteso, il secondo valore effettivo. Il messaggio di errore per [test2] sopra indica che il valore atteso era 2 ma il valore effettivo era 3.
Infine, i messaggi scritti sulla console dai vari metodi di test erano i seguenti:

Questi messaggi mostrano che i metodi [@Before] e [@After] sono stati effettivamente chiamati, rispettivamente, prima e dopo ogni metodo di test.
Le classi di test non sono necessariamente scritte dagli sviluppatori stessi. Possono essere scritte dalle persone che hanno redatto le specifiche dell'applicazione. Alcuni metodi di sviluppo noti come TDD (Test-Driven Development) sostengono la scrittura delle classi di test anche prima di scrivere le classi da testare. Questo a volte aiuta a chiarire specifiche che altrimenti potrebbero essere interpretate in modi diversi.
Creiamo un test JUnit 4, denominato [JUnitTest1VoterList], per la classe [VoterList]. In Eclipse, procederemo come descritto in precedenza:
![]() | ![]() |
Completiamo il codice generato dalla procedura guidata come segue:
package istia.st.elections.tests;
import org.junit.Assert;
import istia.st.elections.ElectionsException;
import istia.st.elections.ListeElectorale;
import org.junit.Test;
public class JUnitTest1ListeElectorale {
@Test
public void t1() {
// electoral list creation
ListeElectorale liste = new ListeElectorale(1, "a", 32000, 0, false);
// checks
Assert.assertEquals("a", liste.getNom());
Assert.assertEquals(32000, liste.getVoix());
Assert.assertEquals(false, liste.isElimine());
Assert.assertEquals(0, liste.getSieges());
// validity check id
boolean erreur = false;
try {
liste.setId(-4);
} catch (ElectionsException e) {
erreur = true;
}
Assert.assertEquals(true, erreur);
// name validity check
erreur = false;
try {
liste.setNom("");
} catch (ElectionsException e) {
erreur = true;
}
Assert.assertEquals(true, erreur);
// voice validity check
erreur = false;
try {
liste.setVoix(-4);
} catch (ElectionsException e) {
erreur = true;
}
Assert.assertEquals(true, erreur);
// seat validity check
erreur = false;
try {
liste.setSieges(-4);
} catch (ElectionsException e) {
erreur = true;
}
Assert.assertEquals(true, erreur);
}
}
L'esecuzione del test produce il seguente risultato:

I test sono stati superati. Considereremo ora la classe [VoterList] operativa.
3.5. MainElections: versione 2
Letture consigliate:
- Sezioni 2.1, 2.2, 2.4 e 2.7 del Capitolo 2 in [1]: Classi e interfacce
- sezioni 3.3 (classe String), 3.5 (classe ArrayList), 3.6 (classe Arrays)
Vogliamo riscrivere l'applicazione [Elections] aggiungendo i seguenti nuovi vincoli:
- Useremo la classe [VoterList] per rappresentare un elenco di candidati
- l'applicazione chiederà all'utente le seguenti informazioni:
- il numero di seggi da assegnare
- i nomi e i voti per le liste. Non sappiamo in anticipo quante liste ci siano. L'ultima lista sarà indicata da un nome uguale alla stringa "*".
- Poiché non conosciamo in anticipo il numero di liste, queste verranno prima memorizzate in un oggetto [ArrayList]. Quindi, una volta inserite tutte le liste, verranno trasferite in un array di liste.
- I risultati saranno visualizzati in ordine decrescente in base al numero di seggi ottenuti.
Per ordinare un array T, possiamo utilizzare vari metodi statici della classe [Arrays]:
- Arrays.sort(T): ordina l'array T in ordine naturale, se ne ha uno (ascendente per numeri, date, alfabetico per stringhe, ecc.)
- Arrays.sort(T,comparator): per ordinare gli array T che non hanno un ordine naturale. È il caso, in questo contesto, dell'array di liste, che deve essere ordinato in base a un campo specifico della lista: il numero di seggi ottenuti.
Nel metodo Arrays.sort(T,comparator), il parametro comparator è un oggetto che implementa la seguente interfaccia Comparator:

- Il metodo compare consente di confrontare due elementi dell'array T
- Il metodo equals determina se due oggetti sono uguali
Entrambi i metodi confrontano i tipi Object obj1 e obj2. Determinare se obj1<obj2, obj1=obj2 o obj1>obj2 dipende dalla relazione di ordinamento che desideriamo stabilire tra i due oggetti. Spetta allo sviluppatore che implementa questa interfaccia specificare come sappiamo che:
- obj1 è minore di obj2
- obj1 è maggiore di obj2
- obj1 è uguale a obj2
La classe Object, da cui derivano tutte le classi Java, dispone già di un metodo [equals]. Per ordinare un array T di oggetti di tipo O, il metodo [equals] della classe O non è utile. Possiamo quindi lasciare l'implementazione predefinita fornita dalla classe Object. Deve quindi essere implementato solo il metodo [compare]. Questo metodo viene chiamato ripetutamente dal metodo [Arrays.sort]. Ogni volta, [Arrays.sort] passa obj1 e obj2 — due elementi dell'array T da ordinare — come parametri al metodo compare. Nel nostro caso, questi elementi saranno di tipo [VoterList]. Si noti il polimorfismo in atto in questo caso. Il metodo [compare] è definito per accettare parametri di tipo [Object]. Ciò significa che può accettare parametri di tipo [Object] o di qualsiasi tipo derivato (polimorfismo). Poiché [Object] è la classe padre di tutte le classi Java, i parametri effettivi possono essere di tipo [VoterList].
Per l'ordinamento in ordine crescente, il metodo [compare] deve restituire:
- -1 se obj1 è minore di obj2
- +1 se obj1 è maggiore di obj2
- 0 se obj1 è uguale a obj2
Per l'ordinamento in ordine decrescente, i valori +1 e -1 sono invertiti. I termini "è minore di", "è maggiore di" e "è uguale a" esprimono una relazione d'ordine. Per gli oggetti di tipo [VoterList], la relazione list1 "è minore di" list2 è vera se list1 ha meno voti di list2.
Nello stesso file sorgente della classe [MainElections], possiamo aggiungere una seconda classe:
- Riga 2: La classe non è dichiarata come pubblica. In un file sorgente Java possono esserci più classi, ma solo una può avere l'attributo public: quella con lo stesso nome del file sorgente.
Nel precedente metodo compare, i parametri sono di tipo Object, il che richiede che le righe 7 e 8 convertano i parametri del metodo dal tipo Object al tipo VoterList. La firma del metodo compare è imposta dall'interfaccia Comparator di , che è stata scritta per confrontare oggetti arbitrari. A partire dal JDK 1.5, esiste un'interfaccia Comparator generica: Comparator<T>, dove T è un tipo Java qualsiasi. Il metodo compare dell'interfaccia Comparator<T> confronta oggetti di tipo T anziché di tipo Object, il che evita i precedenti cast di tipo. La classe di confronto per oggetti di tipo VoterList potrebbe apparire così:
// classe de comparaison de listes électorales
class CompareListesElectorales implements Comparator<ListeElectorale> {
// comparaison de deux listes électorales selon le nombre de sièges
public int compare(ListeElectorale listeElectorale1,
ListeElectorale listeElectorale2) {
...
}
}
- riga 2: la classe implementa l'interfaccia Comparator<VoterList>
- righe 5-6: i parametri del metodo compare sono di tipo VoterList. Il type casting non è più necessario.
Il JDK 1.5 ha introdotto il concetto di classi/interfacce generiche per varie classi/interfacce del JDK 1.4 che inizialmente gestivano solo oggetti di tipo Object. È il caso di liste, dizionari, ...
Abbiamo accennato in precedenza al fatto che, non conoscendo il numero di elenchi, non potevamo memorizzarli in un array. È possibile memorizzarli in un oggetto ArrayList, che implementa il concetto di “elenco di oggetti”. Questa classe memorizza oggetti di tipo Object. A partire da JDK 1.5, sono disponibili elenchi di oggetti tipizzati. Pertanto, useremo un oggetto ArrayList<VoterList> per memorizzare gli elenchi prima di trasferirli in un array. Se l'array è denominato tLists, verrà ordinato utilizzando la seguente istruzione:
// tri des listes
Arrays.sort(tListes, new CompareListesElectorales());
dove CompareVoterLists è la classe che implementa l'interfaccia Comparator<VoterList>.
Compito: riscrivere l'applicazione [Elections] tenendo conto di queste nuove specifiche.
Il progetto Eclipse potrebbe avere questo aspetto:
![]() |
Un esempio dell'esecuzione di [1] è il seguente:
















