Esercizi
- Home
- Esercizi Capitolo 10
Capitolo 10
Tipi Generici e Collezioni
I tipi generici rappresentano un argomento che può diventare molto complesso. Tra questi esercizi,
ed in particolare tra quelli a risposta multipla che supportano la certificazione Java, ne troverete
alcuni davvero complicati, perché per rispondere correttamente dovrete avere una preparazione
eccellente.
Di seguito trovate gli esercizi del capitolo.
Per ogni esercizio, cliccando sulla traccia potete vedere la relativa soluzione.
Gli esercizi caratterizzati dall'icona sono
considerati i più complessi relativamente agli argomenti trattati.
Se preferite lavorare offline, è possibile scaricare tutti gli esercizi e le relative
soluzioni in formato PDF nella sezione download.
-
Esercizio 10.a) Generics, Vero o Falso:
1. I tipi generici e i tipi parametro sono la stessa cosa.
2. Un vantaggio principale dei generics, è che permettono di evitare bachi come quelli provocati da unaClassCastException
al runtime, individuandoli in compilazione.
3. Le collection possono essere usate anche senza specificare i tipi parametro. In tal caso si parla di raw type.
4. L'ereditarietà ignora i tipi parametro.
5. Le wildcard si usano quando non abbiamo tipi parametrici a disposizione da usare.
6. Se creiamo un tipo generico con tipo parametro della classePortaMonete
, possiamo usare lo stesso parametro anche nei metodi dichiarati nel tipo.
7. Nei metodi generici il tipo parametro viene messo come parametro di input del metodo.
8. Il seguente codice:compila senza errori. 9. Il seguente codice:public class MyGeneric <Pippo extends Number> { private List<Pippo> list; public MyGeneric () { list = new ArrayList<Pippo>(); } public void add(Pippo pippo) { list.add(pippo); } public void remove(int i) { list.remove(i); } public Pippo get(int i) { return list.get(i); } public void copy(ArrayList<?> al) { Iterator<?> i = al.iterator(); while (i.hasNext()) { Object o = i.next(); add(o); } } }
compila senza errori.public <N extends Number> void print(List<N> list) { for (Iterator<N> i = list.iterator(); i.hasNext( ); ) { System.out.println(i.next()); } }
10. Per risolvere le wildcard capture bisogna usare un metodo helper definito nella libreria standard.
11. Il seguente codice:compila senza errori.List<String> strings = new ArrayList<String>(); strings.add(new Character('A'));
12. Il seguente codice:compila senza errori.List<int> ints = new ArrayList<int>();
13. Il seguente codice:compila senza errori.List<int> ints = new ArrayList<Integer>();
14. Il seguente codice:compila senza errori.List<Integer> ints = new ArrayList<Integer>(); ints.add(1);
15. Il seguente codice:compila senza errori.List ints = new ArrayList<Integer>();
Soluzione
1. Falso.
2. Vero.
3. Vero.
4. Vero.
5. Vero.
6. Vero.
7. Falso.
8. Falso, otterremo il seguente errore:9. Vero.error: incompatible types: Object cannot be converted to Pippo add(o); ^ where Pippo is a type-variable: Pippo extends Number declared in class MyGeneric
10. Falso, il metodo helper bisogna crearselo da soli.
11. Falso.
12. Falso.
13. Falso.
14. Vero.
15. Vero. -
Esercizio 10.b)
Consideriamo i sorgenti creati con gli esercizi realizzati per il capitolo 5 e poi ridefiniti nei capitoli 8 e 9. Sostituire l'array
monete
definito nella classePortaMonete
conArrayList
di oggettiMoneta
. Eventualmente modificare le altre classi in modo tale che funzioni tutto come prima.
Soluzione
Il listato della classe
PortaMonete
cambierà molto:
import java.util.ArrayList; import java.util.List; /** * Astrae il concetto di portamonete che può contenere un numero * limitato di monete. * * @author Claudio De Sio Cesari */ public class PortaMonete { /** * Costante che rappresenta il numero massimo di monete che questo * portamonete può contenere. */ private static final int DIMENSIONE = 10; /** * Un array list che contiene un numero limitato di monete. */ private final List<Moneta> monete = new ArrayList<>(DIMENSIONE); /** * Crea un oggetto portamonete contenente monete i cui valori sono * specificati dal varargs valori. Se * il numero di elementi del varargs valori è maggiore del numero massimo di * monete che il portamonete corrente può contenere, allora viene gestita * un'eccezione * * @param valori un varargs di valori di monete. */ public PortaMonete(Valore... valori) { assert monete.size() <= DIMENSIONE; try { int numeroMonete = valori.length; for (int i = 0; i < numeroMonete; i++) { if (i >= DIMENSIONE) { throw new PortaMonetePienoException( "Sono state inserite solo le prime + " + DIMENSIONE + " monete!"); } monete.add(new Moneta(valori[i])); } } catch (PortaMonetePienoException exc) { System.out.println(exc.getMessage()); } catch (NullPointerException exc) { System.out.println("Il portamonete è stato creato vuoto"); } assert monete.size() <= DIMENSIONE; } /** * Aggiunge una moneta al portamonete. Se questo è pieno la moneta non è * aggiunta e viene stampato un errore significativo. * * @param moneta la moneta da aggiungere. * @throws PortaMonetePienoException */ public void aggiungi(Moneta moneta) throws PortaMonetePienoException { try { System.out.println("Proviamo ad aggiungere una " + moneta.getDescrizione()); } catch (NullPointerException exc) { throw new MonetaNullException(); } int indiceLibero = primoIndiceLibero(); if (indiceLibero == -1) { throw new PortaMonetePienoException("Portamonete pieno! La moneta " + moneta.getDescrizione() + " non è stata aggiunta..."); } else { monete.set(indiceLibero, moneta); System.out.println("E' stata aggiunta una " + moneta.getDescrizione()); } } /** * Esegue un prelievo della moneta specificata dal portamonete corrente. Nel * caso non sia presente la moneta specificata, un errore significativo * verrà stampato e null verrà ritornato. * * @param moneta la moneta da prelevare. * @return la moneta trovata o null se non trovata. * @throws MonetaNonTrovataException */ public Moneta preleva(Moneta moneta) throws MonetaNonTrovataException { try { System.out.println("Proviamo a prelevare una " + moneta.getDescrizione()); } catch (NullPointerException exc) { throw new MonetaNullException(); } Moneta monetaTrovata = null; int indiceMonetaTrovata = indiceMonetaTrovata(moneta); if (indiceMonetaTrovata == -1) { throw new MonetaNonTrovataException("Moneta non trovata!"); } else { monetaTrovata = moneta; monete.set(indiceMonetaTrovata, moneta); System.out.println("Una " + moneta.getDescrizione() + " prelevata"); } return monetaTrovata; } /** * Stampa il contenuto del portamonete. */ public void stato() { System.out.println("Il portamonete contiene:"); for (Moneta moneta : monete) { if (moneta == null) { break; } System.out.println("Una " + moneta.getDescrizione()); } } /** * Restituisce il primo indice libero nell'array delle monete o -1 se il * portamonete è pieno. * * @return il primo indice libero nell'array delle monete o -1 se il * portamonete è pieno. */ private int primoIndiceLibero() { int indice = monete.indexOf(null); return indice; } private int indiceMonetaTrovata(Moneta moneta) { int indiceMonetaTrovata = -1; final int size = monete.size(); for (int i = 0; i < size; i++) { if (monete.get(i) == null) { break; } Valore valoreMonetaNelPortaMoneta = monete.get(i).getValore(); Valore valore = moneta.getValore(); if (valore == valoreMonetaNelPortaMoneta) { indiceMonetaTrovata = i; break; } } return indiceMonetaTrovata; } }
Il costruttore ora ha delle asserzioni diverse, perché la grandezza di unArrayList
si misura diversamente dalla grandezza di un array, ma ha mantenuto la sua logica algoritmica ed ha cambiato solo il modo in cui viene aggiunto un elemento all'array list (metodoadd
).
Stessa logica anche per il metodoaggiungi
, che ha modificato solo il modo in cui viene aggiunto un elemento con indice specificato (metodoset
).
Il metodo privatoprimoIndiceLibero
è stato notevolmente semplificato, grazie all'utilizzo del metodoindexOf
dell'arraylist
.
Il lettore dovrebbe essere in grado ora di trovare anche le differenze nel metodopreleva
e nel metodoindiceMonetaTrovata
, visto che sono molto simili a quanto abbiamo visto per i metodiaggiungi
eprimoIndiceTrovato
.
Non è necessario modificare nessun'altra classe visto che la variabilemonete
che abbiamo modificato è incapsulata. -
Esercizio 10.c)
Creare una classe astratta
Frutta
che definisce una variabile incapsulatapeso
. Per il nostro programma un oggettoFrutta
senza peso non ha senso. Creare le sue sottoclassiMela
,Pesca
,Arancia
. Creare una classe genericaCesto
che astrae il concetto di cesto della frutta. Questa definisce un array list di frutti. Inoltre deve esporre un metodogetPeso
che ritorna il peso totale del contenuto della cesta; un metodoaggiungi
per aggiungere un frutto alla volta, che rilancia un'eccezione (creata appositamente) nel caso l'aggiunta del frutto che si vuole aggiungere faccia superare il limite massimo di 5 chili di peso. OgniCesta
deve avere un solo tipo di frutta per volta. Implementare una soluzione con i generics. Creare una classe di test per verificare l'effettivo funzionamento del programma.
Soluzione
Il listato della classe
Frutta
e delle sue sottoclassi li riportiamo di seguito:
public abstract class Frutta { private final int peso; public Frutta(int peso) { this.peso = peso; } public int getPeso() { return peso; } } public class Mela extends Frutta { public Mela(int peso) { super(peso); } } public class Pesca extends Frutta { public Pesca(int peso) { super(peso); } } public class Arancia extends Frutta { public Arancia(int peso) { super(peso); } }
La classe che rappresenta l'eccezione potrebbe essere la seguente:
public class PesoException extends Exception { public PesoException(String messaggio) { super(messaggio); } }
La classeCesta
, invece potremmo codificarla così:
import java.util.ArrayList; public class Cesta<F extends Frutta> { private ArrayList<F> frutta; public Cesta() { frutta = new ArrayList<>(); } public ArrayList<F> getFrutta() { return frutta; } public void aggiungiFrutta(F frutto) throws PesoException { final int nuovoPeso = getPeso() + frutto.getPeso(); if (nuovoPeso > 2000) { throw new PesoException("Troppo peso: " + nuovoPeso + " grammi!"); } frutta.add(frutto); System.out.println(frutto.getClass().getName() + " da " + frutto.getPeso() + " grammi aggiunta alla cesta"); } public int getPeso() { int peso = 0; for (F frutto : frutta) { peso += frutto.getPeso(); } return peso; } }
Si noti che con il tipo parametro possiamo usare una qualsiasi sottoclasse diFrutta
. Il tipoF
, viene usato poi nei metodi della classe e sarà sostituito durante l'esecuzione con una sottoclasse diFrutta
.
Infine possiamo codificare una semplice classe di test nella seguente maniera:
public class TestFrutta { public static void main(String args[]) { Cesta<Mela> cestaDiMele = new Cesta<>(); for (int i = 0; i <= 20; i++) { try { Mela mela = new Mela(100); cestaDiMele.aggiungiFrutta(mela); } catch (PesoException exc) { System.out.println(exc.getMessage()); } } } }
Segue l'output dell'esecuzione della classeTestFrutta
:
Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Mela da 100 grammi aggiunta alla cesta Troppo peso: 2100 grammi!
-
Esercizio 10.d)
Quali dei seguenti statement è compilabile (scegliere tutti gli statement corretti):
1.2.HashMap hm = new HashMap();
3.HashMap<> hm = new HashMap<>();
4.HashMap<Integer, String> hm = new HashMap ()<Integer, String>;
5.HashMap<Double> hm = new HashMap<Double>();
HashMap<Double> hm = new HashMap<>();
Soluzione
La risposta corretta è la numero 1. La numero 2 è errata perché l'operatore diamond si utilizza sull'istanza non sul reference. La numero 3 è errata per l'ordine errato delle parentesi tonde e i parametri generici dell'istanza (invertendo l'ordine sarebbe stato uno statement compilabile). Le ultime due sono errate perché un
HashMap
ha sempre due parametri. -
Esercizio 10.e)
Quali dei seguenti statement è compilabile (scegliere tutti gli statement corretti):
1.2.ArrayList al = new ArrayList<Integer>();
3.ArrayList<Integer> al = new ArrayList<>();
4.ArrayList<String, String> al = new ArrayList<String, String>();
5.ArrayList al = new ArrayList<>();
ArrayList al = new ArrayList<int>();
Soluzione
Le risposte corrette sono la numero 1, la numero 2 e la numero 4. La numero 4 in particolare è un caso particolare perché si tratta di un raw type che usa l'operatore diamond, che in questo particolare caso è praticamente opzionale. La numero 3 è errata perché un
ArrayList
ha sempre un unico parametro generico. -
Esercizio 10.f)
Quali dei seguenti statement è compilabile (scegliere tutti gli statement corretti):
1.2.List al = new ArrayList<HashMap<String, String>>();
3.Map<ArrayList<String>> hm = new ArrayList<>();
4.List<String, String> al = new ArrayList<HashMap<String, String>>();
5.List<Map<List<List<Integer>>, String>> al = new ArrayList<>();
List<Map<String, String>> al = new ArrayList<HashMap<String, String>>();
Soluzione
Le risposte corrette sono la numero 1 e la numero 4. La numero 2 è scorretta perché una mappa ha sempre due parametri. La numero 3 non compila perché non c'è coincidenza tra i parametri del reference (due stringhe, e questo è già un errore perché una lista ha sempre un solo parametro) e l'istanza (un
HashMap
). La numero 5 è errata perché i parametri generici tra reference e istanza non coincidono. InfattiMap
non coincide conHashMap
. Ecco infatti l'output dell'esecuzione:
jshell> List<Map<String, String>> al = new ArrayList<HashMap<String, String>>() ; | Error: | incompatible types: java.util.ArrayList< java.util.HashMap<java.lang.String,java.lang.String>> cannot be converted to java.util.List<java.util.Map< java.lang.String,java.lang.String>> | List<Map<String, String>> al = new ArrayList<HashMap<String, String>>(); | ^--------------------------------------^
-
Esercizio 10.g)
Qual è l'output delle seguenti righe di codice?
import java.util.*; public class Esercizio10G { public static void main(String args[]) { List list = new ArrayList(); list.add("1"); list.add('2'); list.add(3); Iterator iterator = list.iterator(); while (iterator.hasNext()) { System.out.print(iterator.next()); } } }
1.2.123
3. Nessuno, il programma non si può compilare perché non è possibile aggiungere elementi diversi alla collezione.321
4."1"'2'3
5. Nessuno, il programma non si può compilare perché non è possibile recuperare un
Iterator
da una collezione eterogenea.
6. L'esecuzione sarà stoppata nel momento in cui si proverà a stampare il secondo valore della collezione.
Soluzione
La risposta corretta è la 1. Infatti non avendo specificato il parametro generico (si parla in questi casi di raw type), sarà possibile aggiungere elementi di ogni tipo alla collezione. Si noti che aggiungendo parametri primitivi, questi vengono automaticamente promossi ai relativi oggetti wrapper (in questo caso '2' viene promossa a
Character
, mentre 3 adInteger
). Segue l'output dell'esecuzione:
123
-
Esercizio 10.h)
Qual è l'output delle seguenti righe di codice?
import java.util.*; public class Esercizio10H { public static void main(String args[]) { List<Object> list = new ArrayList<>(); list.add("1"); list.add('2'); list.add(3); Iterator<Object> iterator = list.iterator(); while (iterator.hasNext()) { System.out.print(iterator.next()); } } }
1.2.123
"1"'2'3
3. Nessuno, il programma non si può compilare perché la collezione ammette solo oggetti di tipo
Object
.
4. Nessuno, il programma non si può compilare perché non è possibile recuperare unIterator
da una collezione eterogenea.
5. L'esecuzione sarà stoppata nel momento in cui si proverà a stampare il secondo valore della collezione.
Soluzione
La risposta corretta è la 1. Segue l'output dell'esecuzione:
123
-
Esercizio 10.i)
Qual è l'output delle seguenti righe di codice?
import java.util.*; public class Esercizio10I { public static void main(String args[]) { List<String> list = new ArrayList<>(); list.add("1"); list.add(null); list.add('3'); Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { System.out.print(iterator.next()); } } }
1. Nessuno, il programma non si può compilare perché la collezione non ammette valori
null
.
2. Nessuno, il programma non si può compilare perché la collezione non ammette valori primitivi.
3. L'esecuzione sarà stoppata nel momento in cui si proverà ad aggiungerenull
alla collezione.
4. L'esecuzione sarà stoppata nel momento in cui si proverà ad aggiungere'3'
alla collezione.
5.1null3
Soluzione
La risposta corretta è la 2. Non è possibile aggiungere un tipo primitivo (né altri tipi complessi diversi da
String
) ad una collezione che è parametrizzata con un tipoString
. Infatti l'output della compilazione è il seguente:
Esercizio10I.java:8: error: no suitable method found for add(char) list.add('3'); ^ method Collection.add(String) is not applicable (argument mismatch; char cannot be converted to String) method List.add(String) is not applicable (argument mismatch; char cannot be converted to String) Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output 1 error
-
Esercizio 10.j)
Creare una classe che, dato un array di interi, li ordina dal più alto al più basso.
Soluzione
Se parliamo di un array di interi, esistendo l'autoboxing-unboxing ci possiamo anche rifare ad un array di
Integer
. Visto che questa classe è dichiaratafinal
, e che quindi non si può estendere, è impensabile crearne una sottoclasse che implementaComparable
. L'unica soluzione che ci rimane è creare una classeComparator
che ordina gli interi in maniera inversa:
import java.util.Comparator; public class IntegerComparator implements Comparator<Integer> { @Override public int compare(Integer o1, Integer o2) { return -(o1.compareTo(o2)); } }
La classe di test potrebbe limitarsi a questa:
import java.util.Arrays; public class TestIntegerComparator { public static void main(String args[]) { Integer []array = {1942, 1947, 1971, 1984, 1976, 1974}; Arrays.sort(array, new IntegerComparator()); for (int intero : array) { System.out.println(intero); } } }
il cui output sarà:
1984 1976 1974 1971 1947 1942
-
Esercizio 10.k)
Creare una classe
WrapperComparable
che abbia le seguenti caratteristiche:
• Abbia come costante incapsulata unInfine creare una classe che testi che l'ordinamento funzioni come ci si aspetta.Integer
che deve essere sempre valorizzato.
• Abbia un metodotoString
che restituisca l'intero.
• Definisca un modo di ordinamento che vada dall'intero incapsulato più alto al più basso.
Soluzione
Il listato per la classe
WrapperComparable
dovrebbe essere definito così:
public class WrapperComparable implements Comparable<WrapperComparable> { private Integer integer; public WrapperComparable(Integer integer) { if (integer == null) { integer = 0; } this.integer = integer; } public Integer getInteger() { return integer; } public void setInteger(Integer integer) { this.integer = integer; } @Override public int compareTo(WrapperComparable otherWrapperComparable) { return -(integer.compareTo(otherWrapperComparable.getInteger())); } @Override public String toString() { return "WrapperComparable("+integer+ ")"; } }
Il listato per la classeTestWrapperComparable
potrebbe essere il seguente:
import java.util.Arrays; public class TestWrapperComparable { public static void main(String args[]) { WrapperComparable[] array = {new WrapperComparable(1942), new WrapperComparable(1974), new WrapperComparable(1907)}; Arrays.sort(array); for (WrapperComparable wrapperComparable : array) { System.out.println(wrapperComparable); } } }
-
Esercizio 10.l)
Qual è l'output delle seguenti righe di codice?
import java.util.*; public class Esercizio10L { public static void main(String args[]) { Map<Integer, Integer> map = new HashMap<>(1); map.put(14, 12); map.put('a','b'); map.put(07,0x0ABC); } }
1. Il codice non compila perché non è possibile aggiungere tre coppie chiave-valore ad una collection di dimensione 1.
2. Il codice non compila perché i valori07
e0x0ABC
non sono valoriinteri.
3. Il codice non compila perché i valori'a'
e'b'
non sono valori interi.
4. Nessuna delle precedenti.
Soluzione
L'unica affermazione corretta è la numero 3.
L'affermazione numero 1 non è valida perché le collezioni sono ridimensionabili di default, e il valore1
passato al costruttore dell'HashMap
, rappresenta solo la dimensione iniziale della collection.
Anche l'affermazione numero 2 non è corretta. Infatti i valori07
e0x0ABC
sono valori interi e quindi con l'autoboxing sono correttamente "wrappati" all'interno di oggettiInteger
.
Invece i valori'a'
e'b'
di cui si parla nell'affermazione 3 sono caratteri e sono "wrappati" all'interno di oggettiCharacter
.
Segue l'output della compilazione della classeEsercizio10L
:
Esercizio10L.java:7: error: incompatible types: char cannot be converted to Integer map.put('a','b'); ^ Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output 1 error
-
Esercizio 10.m)
Data la seguente dichiarazione:
Map<String, Float> map = new Hashmap<>(1);
Quali tra i seguenti statement sono validi?
1.2.map.add("stringa", 1.1F);
3.map.add("stringa", 1.1D);
4. Nessuno dei precedenti.map.add("stringa", 1);
Soluzione
La risposta è l'ultima, visto che per aggiungere coppie chiave-valore ad una mappa, si usa il metodo
put
, non il metodoadd
(che è un metodo dichiarato nelle liste). -
Esercizio 10.n)
Data la seguente classe:
public class Esercizio10N/*INSERISCI CODICE 1*/ { public static void main(String[] args) { Esercizio10N<String> g = new Esercizio10N/*INSERISCI CODICE 2*/(); Esercizio10N<Object> g2 = new Esercizio10N/*INSERISCI CODICE 3*/(); } }
Se volessimo inserire le tre istruzioni mancanti affinché la compilazione vada a buon fine, quali tra queste opzioni risulteranno essere valide?
1. Codice 1:<Object>;
Codice 2:<Object>
Codice 3:<Object>
.
2. Codice 1:<>;
Codice 2:<>;
Codice 3:<>
.
3. Codice 1: nessun codice; Codice 2:<>;
Codice 3:<>
.
4. Codice 1:<T extends Object>;
Codice 2:<String>;
Codice 3:<Object>
.
5. Codice 1:<? extends Object>;
Codice 2:<String>;
Codice 3:<Object>
.
6. Codice 1:<T>;
Codice 2:<String>;
Codice 3:<>
.
7. Codice 1:<T>;
Codice 2:<T>;
Codice 3:<Object>
.
Soluzione
Le opzioni corrette sono la 4 e la 6.
L'opzione numero 1 è errata perché non si può assegnare un reference con tipo genericoString
, ad un'istanza con tipo genericoObject
. Infatti, compilando il file si ottiene il seguente output:
Esercizio10N.java:2: error: incompatible types: Esercizio10N<Object> cannot be converted to Esercizio10N<String> Esercizio10N<String> g = new Esercizio10N<Object>(); ^ where Object is a type-variable: Object extends java.lang.Object declared in class Esercizio10N 1 error
La numero 2 fallirà alla prima riga, perché non è possibile utilizzare un operatore diamond come tipo generico di una classe. La numero 3 fallirà anch'essa in compilazione perché non abbiamo dichiarato un tipo generico per la classe. Segue l'output che riporta gli errori derivanti dal processo di compilazione:
Esercizio10N.java:2: error: type Esercizio10N does not take parameters Esercizio10N<String> g = new Esercizio10N<>(); ^ Esercizio10N.java:3: error: type Esercizio10N does not take parameters Esercizio10N<Object> g2 = new Esercizio10N<>(); ^ Esercizio10N.java:2: error: cannot infer type arguments for Esercizio10N Esercizio10N<String> g = new Esercizio10N<>(); ^ reason: cannot use '<>' with non-generic class Esercizio10N Esercizio10N.java:3: error: cannot infer type arguments for Esercizio10N Esercizio10N<Object> g2 = new Esercizio10N<>(); ^ reason: cannot use '<>' with non-generic class Esercizio10N 4 errors
La numero 5 è anch'essa errata. Infatti le wildcard si utilizzano con i parametri di metodi. Segue l'output della compilazione:
Esercizio10N.java:1: error: <identifier> expected public class Esercizio10N<? extends Object> { ^ 1 error
La numero 7 infine, produce il seguente output in compilazione:
Esercizio10N.java:2: error: incompatible types: Esercizio10N<T> cannot be converted to Esercizio10N<String> Esercizio10N<String> g = new Esercizio10N<T>(); ^ where T is a type-variable: T extends Object declared in class Esercizio10N 1 error
dove veniamo avvisati cheEsercizio10M<T>
, non può essere convertito aEsercizio10N<String>
. -
Esercizio 10.o)
Data la seguente classe:
import java.util.*; public class Esercizio10O { public static int getSize(List/*INSERISCI CODICE QUI*/ list) { return list.size(); } public static void main(String args[]) { System.out.println(getSize(new ArrayList<Integer>())); System.out.println(getSize( new ArrayList<HashMap<String, List<String>>>())); } }
Quali tra i seguenti parametri generici potrebbero sostituire il commento
/*INSERISCI CODICE QUI*/
per far sì che il codice compili correttamente, e permetta di stampare la dimensione della lista passata in input, qualsiasi sia il tipo di lista?
1.2.<Object>
3.<>
4.<?>
5.<T extends Object>
6.<? extends Object>
7. Nessun codice, il file può essere compilato così com'è.<T>
Soluzione
Le opzioni corrette sono la 3 e la 5, che sono sostanzialmente equivalenti, ma anche la 7 dove utilizziamo un raw type. Infatti compilando utilizzando l'opzione 7 otterremo un warning:
Esercizio10O.java:4: warning: [rawtypes] found raw type: List public static int getSize(List/*INSERISCI CODICE QUI*/ list) { ^ missing type arguments for generic class List<E> where E is a type-variable: E extends Object declared in interface List 1 warning
L'opzione 1 è errata, perché il parametroObject
non ammette altri tipi parametri al di fuori diObject
. Infatti ecco l'output:
Esercizio10O.java:9: error: incompatible types: ArrayList<Integer> cannot be converted to List<Object> System.out.println(getSize(new ArrayList<Integer>())); ^ Esercizio10O.java:10: error: incompatible types: ArrayList<HashMap<String,List<String>>> cannot be converted to List<Object> System.out.println(getSize(new ArrayList< HashMap<String, List<String>>>())); ^ Note: Some messages have been simplified; recompile with -Xdiags:verbose to get full output 2 errors
L'opzione 2 non ha senso (sintassi errata). Infatti ecco l'output:
Esercizio10O.java:4: error: illegal start of type public static int getSize(List<> list) { ^ 1 error
L'opzione 4 e l'opzione 6 sono entrambe errate per lo stesso motivo. Il tipoT
non è definito da nessuna parte. Segue l'output con l'opzione 4:
Esercizio10O.java:4: error: cannot find symbol public static int getSize(List<T> list) { ^ symbol: class T location: class Esercizio10O 1 error
Che è praticamente identico a quello dove si utilizza l'opzione 6:
Esercizio10O.java:4: error: > expected public static int getSize(List<T extends Object> list) { ^ 1 error
-
Esercizio 10.p)
Dato il seguente codice:
public class Esercizio10P<T> { public static T[] getValues(T t) { return t.values(); } private enum T { T1, T2, T3; } }
Quale tra le seguenti affermazioni sono corrette?
1. Il codice non compila perché non è possibile chiamare l'enumerazione innestataT
come il tipo parametro.
2. Il codice compila correttamente.
3. Il codice compila correttamente, ma mostra un warning.
4. Il codice non compila perché non è possibile dichiarare un'enumerazione come tipo parametro.
5. Il codice non compila perché un metodo statico non può accedere ad una enumerazione non statica.
6. Nessuna delle precedenti.
Soluzione
L'affermazione corretta è la numero 3. Infatti l'output è il seguente:
Esercizio10P.java:4: warning: [static] static method should be qualified by type name, T, instead of by an expression return t.values(); ^ 1 warning
Ovvero il compilatore ci sta avvertendo che il metodo values è statico e quindi si dovrebbe chiamare utilizzando il nome dell'enumerazione, e non su un suo elemento.
Per il resto delle risposte è facile capire perché non siano corrette. In particolare per la numero 1 la letteraT
è proprio l'identificatore dell'enumerazione, quindi la domanda è posta in maniera completamente errata. -
Esercizio 10.q)
Data la seguente classe:
public class Esercizio10Q/*INSERISCI CODICE QUI*/ { public Integer max(N n1, N n2) { return Integer.max(n1.intValue(), n2.intValue()); } }
Quale tra i seguenti parametri generici potrebbe sostituire il commento
/*INSERISCI CODICE QUI*/
per far sì che il codice compili correttamente:
1.2.<Object>
3.<Number>
4.<?>
5.<?>
6.<N>
7.<N extends Number>
8. Nessun codice, il file può essere compilato così com'è.<T>
Soluzione
L'opzione corretta è la numero 6. Laddove non c'è il bounded parameter di tipo
Number
, otterremo un errore in compilazione come il seguente:
Esercizio10Q.java:4: error: cannot find symbol return Integer.max(n1.intValue(), n2.intValue()); ^ symbol: method intValue() location: variable n1 of type N where N is a type-variable: N extends Object declared in class Esercizio10Q Esercizio10Q.java:4: error: cannot find symbol return Integer.max(n1.intValue(), n2.intValue()); ^ symbol: method intValue() location: variable n2 of type N where N is a type-variable: N extends Object declared in class Esercizio10Q 2 errors
-
Esercizio 10.r)
Data la seguente classe:
import java.util.List; import java.util.ArrayList; public class Esercizio10R<T> { public static void main(String args[]) { Esercizio10R<Void> e = new Esercizio10R<>(); ArrayList<Integer> a = new ArrayList<>(); a.add(2); a.add(6); a.add(10); a.add(24); a.add(17); System.out.println("La somma degli elementi della lista è " + e.sumElements(a)); } }
Creare un metodo generico (cfr. paragrafo 10.3.4) che:• prenda in input un parametro genericoL
definito dal metodo, che rappresenta una lista di interi. • Abbia come tipo di ritorno un intero, risultato della somma di tutti gli elementi della lista di interi.
Si noti che abbiamo utilizzato il tipo Void come generico per l'istanza diEsercizio10R
, visto che comunque la classeEsercizio10R
non utilizza nemmeno il parametro.
Soluzione
La soluzione potrebbe essere la seguente:
import java.util.List; import java.util.ArrayList; public class Esercizio10R<T> { private <L extends List<Integer>> Integer sumElements(L list) { int size = list.size(); int result = 0; for (int i = 0; i < size; i++) { Integer item = list.get(i); result += item; } return result; } public static void main(String args[]) { Esercizio10R<Void> e = new Esercizio10R<>(); ArrayList<Integer> a = new ArrayList<>(); a.add(2); a.add(6); a.add(10); a.add(24); a.add(17); System.out.println("La somma degli elementi della lista è " + e.sumElements(a)); } }
-
Esercizio 10.s)
Data la seguente gerarchia:
public interface Tecnologia { void stampa(); } public class Inkjet implements Tecnologia { @Override public void stampa() { System.out.println("Stampa getto d'inchiostro"); } } public class Laser implements Tecnologia { @Override public void stampa() { System.out.println("Stampa laser"); } }
Creare una classe generica
Stampante
parametrizzata con una tecnologia, che dichiara a sua volta un metodostampa
, che però delega alla propria tecnologia l'effettivo messaggio da stampare. Creare anche una classe che testa l'effettivo funzionamento del nostro codice.
Soluzione
Una possibile soluzione è rappresentata dal seguente codice, che dichiara un classico bounded parameter, e, come richiesto, dichiara il metodo stampa che delega il suo reale funzionamento all'implementazione del metodo stampa della tecnologia:
public class Stampante<T extends Tecnologia> { private T tecnologia; public Stampante(T tecnologia) { this.tecnologia = tecnologia; } public void stampa() { tecnologia.stampa(); } }
L'effettivo funzionamento può essere testato dalla seguente classe di test:
public class TestStampante { public static void main(String args[]) { Stampante<Laser> stampante = new Stampante<>(new Laser()); stampante.stampa(); Stampante<Inkjet> stampante2 = new Stampante<>(new Inkjet()); stampante2.stampa(); } }
che produce il seguente output:
Stampa laser Stampa getto d'inchiostro
-
Esercizio 10.t)
Data la seguente enumerazione:
public enum Operatore { SOMMA("+"), SOTTRAZIONE("-"), MOLTIPLICAZIONE("X"), DIVISIONE(":"); String segno; Operatore(String segno) { this.segno = segno; } public String toString() { return segno; } }
e la classe:
public class TestOperazione { public static void main(String args[]) { Operazione<Integer, Operatore> operazione = new Operazione<>(1, Operatore.SOMMA, 1); operazione.stampa(); } }
creare la classe
Operazione
, con un metodo stampa che stampi semplicemente:
1 + 1
Soluzione
La classeOperazione
potrebbe essere la seguente:
public class Operazione<Integer, O extends Operatore> { private Integer operando1; private O operatore; private Integer operando2; public Operazione(Integer operando1, O operatore, Integer operando2) { this.operando1 = operando1; this.operatore = operatore; this.operando2 = operando2; } public void stampa() { System.out.println(operando1 + " " + operatore + " " + operando2); } }
-
Esercizio 10.u)
Data la seguente classe:
import java.util.List; import java.util.Iterator; public class Esercizio10U { public static <T extends Number> void cicla(List<T> list) { for (Iterator<T> i = list.iterator(); i.hasNext( ); ) { System.out.println(i.next()); } } }
Quali delle seguenti affermazioni riguardo il metodo cicla sono corrette?
1. È un metodo che usa una wildcard lower bounded.
2. È un metodo che usa un parametro bounded.
3. È un metodo che usa una wildcard upper bounded.
4. È un metodo generico.
5. È un metodo che usa una wildcard capture.
6. È un metodo helper.
Soluzione
Le affermazioni corrette sono la 2 e la 4.
-
Esercizio 10.v)
Data la seguente classe:
import java.util.List; public class Esercizio10V { protected final void modifica(List<?> list) { list.add(list.get(0)); } }
Quali delle seguenti affermazioni riguardo il metodo modifica sono corrette?
1. È un metodo che non compila.
2. È un metodo generico.
3. È un metodo helper.
4. Ha bisogno di un metodo helper.
Soluzione
Le affermazioni corrette sono la 1 e la 4. In particolare bisognerebbe creare un metodo helper come il seguente:
import java.util.List; public class Soluzione10V { protected final void modifica(List<?> list) { helperMethod(list); } private <T> void helperMethod(List<T> list){ list.add(list.get(0)); } }
-
Esercizio 10.w)
Creare un programma che simula il gioco noto come "sasso-carta-forbici", noto anche come "morra cinese". Le istruzioni del gioco potrete trovarle a questo link: https://it.wikipedia.org/wiki/Morra_cinese.
Lanciato il programma l'utente deve specificare se scegliere sasso, carta o forbice, e contemporaneamente il programma deve fare la sua scelta (casualmente).
Si cerchi di utilizzare tutti i concetti appresi sinora per creare quest'applicazione (comprese le enumerazioni). Ricordarsi che è difficile fare delle scelte, ma con i metodi di analisi che abbiamo visto in precedenza possiamo procedere con più rigore e sicurezza.
Soluzione
La nostra soluzione consiste nel creare un'enumerazione
Segno
, che definisce i tre segni del gioco della morra cinese:
public enum Segno { SASSO, CARTA, FORBICI; }
Poi abbiamo creato una classe che abbiamo chiamatoRegole
, che rappresenta il "nucleo di business" del gioco. Qui è presente l'algoritmo che definisce il vincitore. Abbiamo definito tale algoritmo implementando il metodocompare
dell'interfacciaComparator
. La scelta è ricaduta su tale metodo più per scopi didattici che per reale utilità, probabilmente esistevano soluzioni migliori:
import java.util.Comparator; public class Regole implements Comparator<Segno> { @Override public int compare(Segno segno1, Segno segno2) { int risultato = 0; switch (segno1) { case CARTA: { if (segno2 == Segno.SASSO) { risultato = 1; } else if (segno2 == Segno.FORBICI) { risultato = -1; } } break; case SASSO: { if (segno2 == Segno.FORBICI) { risultato = 1; } else if (segno2 == Segno.CARTA) { risultato = -1; } } break; case FORBICI: { if (segno2 == Segno.CARTA) { risultato = 1; } else if (segno2 == Segno.SASSO) { risultato = -1; } } break; default: { risultato = 0; } break; } return risultato; } }
La classe più importante è la classeMorraCinese
che definisce un unico metodo pubblicogioca
, e tre metodi privati:
•getSegno
che recupera un elemento dell'enumerazioneSegno
in base alla sua posizione (id).
•getSegnoComputer
che riutilizza il metodogetSegno
passandogli un numero casuale tra 0 e 2.
•stabilisciVincitore
che restituisce la stringa da stampare come output del programma, basandosi sulla comparazione definita nell'oggettoRegole
.
import java.util.Random; public class MorraCinese { public void gioca(int id) { Segno segnoGiocatore = getSegno(id); Segno segnoComputer = getSegnoComputer(); System.out.println(segnoGiocatore +" VS "+ segnoComputer); String risultato = stabilisciVincitore(segnoGiocatore, segnoComputer); System.out.println(risultato); } private String stabilisciVincitore(Segno segnoGiocatore, Segno segnoComputer) { Regole regole = new Regole(); int risultato = regole.compare(segnoGiocatore, segnoComputer); if (risultato > 0) { return "Vince " + segnoGiocatore + "!\nHai vinto!"; } else if (risultato < 0) { return "Vince " + segnoComputer + "!\nHai perso!"; } else { return "Pari!"; } } private Segno getSegno(int id) { Segno[] segni = Segno.values(); Segno segnoComputer = segni[id]; return segnoComputer; } private Segno getSegnoComputer() { Random random = new Random(); return getSegno(random.nextInt(3)); } }
Infine la classe contenente il metodomain
, gestisce un'eventuale input da riga di comando dell'utente e chiama il metodo gioca diMorraCinese
:
import java.util.Random; public class Esercizio10W { public static void main(String args[]) { int id = getId(args); MorraCinese morraCinese = new MorraCinese(); morraCinese.gioca(id); } public static int getId(String args[]) { int id = 0; if (args.length != 0) { try { id = Integer.parseInt(args[0]); if (id < 0 || id > 2) { System.out.println("Inserire un numero compreso tra 0 e 2"); System.exit(1); } } catch (Exception exc) { System.out.println("Input non valido: "+ args[0]); System.exit(1); } } else { id = new Random().nextInt(3); } return id; } }
Ecco qualche esempio di output:
SASSO VS FORBICI Vince SASSO! Hai vinto! FORBICI VS CARTA Vince FORBICI! Hai vinto! SASSO VS SASSO Pari! FORBICI VS SASSO Vince SASSO! Hai perso!
-
Esercizio 10.z)
Data la seguente classe:
public class Esercizio10Z { public static <E extends Exception> void printException(E e) { System.out.println(e.getMessage()); } public static void main(String[] args) { /*INSERISCI CODICE QUI*/ } }
Quale tra i seguenti statement potrebbe sostituire il commento
/*INSERISCI CODICE QUI*/
per far sì che il codice compili correttamente:1.2.Esercizio10Z.<Exception>printException(new Exception("Exception"));
3.Esercizio10Z.printException(new ClassCastException("ClassCastException "));
4.Esercizio10Z.printException(new RuntimeException("RuntimeException "));
Esercizio10Z.printException(new Throwable("Exception"));
La soluzione di questo esercizio riporta una sintassi complessa che non è stata riportata nelle pagine del libro cartaceo. In pratica, possiamo anticipare che la sintassi al punto 1 è valida. Tale sintassi è usata raramente, e si è preferito non introdurla per non appesantire troppo l'argomento con caratteristiche meno usate. Ricordiamo che è previsto un volume complementare che dovrebbe essere pubblicato contemporaneamente alla release 17 di Java, che sarà dedicato alle novità introdotte dalla prossima versione LTS (cfr. appendice A), e alle caratteristiche avanzate.
Soluzione
Gli statement corretti sono i primi 3, solo l'uso dello statement 4 causerebbe un errore in compilazione, segue l'output:
Esercizio10Z.java:6: error: method printException in class Esercizio10Z cannot be applied to given types; /*INSERISCI CODICE QUI*/Esercizio10Z.printException( new Throwable("Exception")); ^ required: E found: Throwable reason: inference variable E has incompatible bounds upper bounds: Exception lower bounds: Throwable where E is a type-variable: E extends Exception declared in method <E>printException(E) 1 error
Nel caso della risposta numero 1, abbiamo usato una sintassi che si utilizza raramente.