Esercizi
- Home
- Esercizi del capitolo 13 e dell'appendice F
Esercizi del capitolo 13 e dell'appendice F
Espressioni Lambda e
package java.lang
Le espressioni lambda e i reference a metodi non rappresentano un argomento semplice, ma sono
indubbiamente una soluzione molto utile a certi problemi di programmazione. I seguenti esercizi
dovrebbero permettere al lettore di comprendere meglio gli argomenti trattati nel capitolo 13. In
questa sezione inoltre troverete anche esercizi riguardanti la libreria java.lang così come
descritta nell'appendice F. Tali esercizi hanno una numerazione diversa che inizia con la lettera F.
Anche in questo capitolo sono stati introdotti diversi esercizi che supportano la certificazione
Oracle.
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 13.a) Espressioni lambda e reference a metodi:
1. Con un'espressione lambda è possibile fare tutto quello che si può fare con una classe anonima.
2. Un'espressione lambda può utilizzare in maniera thread-safe le variabili d'istanza della classe in cui è dichiarata.
3. Un'espressione lambda può utilizzare le variabili d'istanza della classe in cui è definita solo se dichiarate final.
4. La seguente espressione lambda è legale:5. La seguente espressione lambda è legale:Consumer <String> c = ((x)->System.out.println(x));
6. Il seguente reference a metodo è legale:(x, y) -> System.out.println(x); System.out.println(y);
7. Il seguente statement è legale:System.out::println()
8. Il seguente statement è legale:System.out.println(Math::random)
9. Il seguente statement è legale:System.out::println(Math::random)
10. Supponendo che la classe Chitarra abbia un costruttore senza parametri, allora il seguente codice è legale:Supplier<Chitarra> chitarraSupplier = Chitarra::new;
Chitarrista chitarrista = new Chitarrista(); chitarrista.suonaChitarra(Chitarra::new);
Soluzione
1. Falso, per esempio in una classe anonima possiamo definire anche variabili d'istanza.
2. Falso.
3. Falso.
4. Vero.
5. Falso, mancano le parentesi graffe che circondano le istruzioni del metodo.
6. Falso, le parentesi vicino al metodoprintln
non fanno parte della sintassi.
7. Falso.
8. Falso.
9. Vero.
10. Vero. -
Esercizio 13.b)
Creare una classe
TestComparators
contenente un metodomain
che dichiara un array di stringhe (di cui almeno due con la stessa lunghezza). Creare degli oggettiComparator
con espressioni lambda, per produrre ordinamenti secondo i seguenti criteri:• in ordine di lunghezza (dalla stringa più lunga alla più breve);
• in ordine di lunghezza al contrario (dalla stringa più breve alla più lunga);
• in ordine alfabetico (usare comunque un'espressione lambda o un reference a metodo, anche se non ce ne sarebbe bisogno);
• in ordine alfabetico inverso;
• in ordine di lunghezza e in caso di stessa lunghezza in ordine alfabetico.Ordinare l'array mediante il metodo
sort
della classeArrays
, che prende in input come primo parametro l'array dichiarato e come secondo parametro un oggetto di tipoComparator
. Dopo ogni ordinamento stampare il risultato. Creare questa classe in un package come per esempiocom.claudiodesio.lambda.test
.
Soluzione
Il listato richiesto potrebbe essere come il seguente:
package com.claudiodesio.lambda.test; import java.util.Arrays; import java.util.Comparator; public class TestComparators { static String nomi[] = {"Clarissa", "Jem", "Top", "Ermeringildo", "Iamaca", "Tom", "Arlequin", "Francesca", "Cumbus", "Blue"}; public static void main(String args[]) { Comparator<String> comparatorLunghezza = (first, second) -> -(Integer.compare(first.length(), second.length())); Comparator<String> comparatorLunghezzaAlContrario = (first, second) -> (Integer.compare(first.length(), second.length())); Comparator<String> comparatorAlfabeticoAlContrario = (first, second) -> -(first.compareTo(second)); Comparator<String> comparatorLunghezzaEAlfabeticoAlContrario = (first, second) -> { int result = -Integer.compare(first.length(), second.length()); if (result == 0) { result = first.compareTo(second); } return result; }; Arrays.sort(nomi, comparatorLunghezza); System.out.println("Nomi ordinati per lunghezza: " + Arrays.asList(nomi)); Arrays.sort(nomi, comparatorLunghezzaAlContrario); System.out.println("Nomi ordinati per lunghezza al contrario: " + Arrays.asList(nomi)); Arrays.sort(nomi, String::compareTo); System.out.println("Nomi ordinati: " + Arrays.asList(nomi)); Arrays.sort(nomi, comparatorAlfabeticoAlContrario); System.out.println("Nomi ordinati al contrario: " + Arrays.asList(nomi)); Arrays.sort(nomi, comparatorLunghezzaEAlfabeticoAlContrario); System.out.println( "Nomi ordinati per lunghezza al contrario e in ordine alfabetico: " + Arrays.asList(nomi)); } }
Si noti che il metodoasList
della classeArrays
è stato usato solo per sfruttare la rappresentazione testuale della collezione.
L'output sarà:
Nomi ordinati per lunghezza: [Ermeringildo, Francesca, Clarissa, Arlequin, Iamaca, Cumbus, Blue, Jem, Top, Tom] Nomi ordinati per lunghezza al contrario: [Jem, Top, Tom, Blue, Iamaca, Cumbus, Clarissa, Arlequin, Francesca, Ermeringildo] Nomi ordinati: [Arlequin, Blue, Clarissa, Cumbus, Ermeringildo, Francesca, Iamaca, Jem, Tom, Top] Nomi ordinati al contrario: [Top, Tom, Jem, Iamaca, Francesca, Ermeringildo, Cumbus, Clarissa, Blue, Arlequin] Nomi ordinati per lunghezza al contrario e in ordine alfabetico: [Ermeringildo, Francesca, Arlequin, Clarissa, Cumbus, Iamaca, Blue, Jem, Tom, Top]
-
Esercizio 13.c)
Creare una classe
Citta
(non è possibile utilizzare le lettere accentate negli identificatori in Java), che astrae il concetto di città, che dichiara come variabili incapsulate la stringanome
, e i booleanicapoluogo
, ediMare
.
Si ricorda che i metodi getter per i booleani solitamente usano come prefisso "is" in luogo di "get". Quindi i metodigetCapoluogo
egetDiMare
, si dovrebbero scrivere comeisDiMare
eisCapoluogo
.Creare anche eventuali metodi di utilità come
toString
(affinché ritorni solo il nome) e un costruttore. Questa classe apparterrà ad un package come per esempiocom.claudiodesio.lambda.dati
.
Creare una classeEsercizio13C
con un metodomain
, che stampi:
• la lista di città di mare (per esempioCittà di mare: [Siracusa, Napoli, Pescara, Taranto]
)
• la lista di città capoluogo (per esempioCittà capoluogo: [Milano, Potenza, Perugia, Napoli]
)
sfruttando i metodi a reference e l'interfaccia
Predicate
.
Questa classe apparterrà ad un package come per esempiocom.claudiodesio.lambda.test
.
Soluzione
Il listato della classe
Citta
, dovrebbe essere il seguente:
package com.claudiodesio.lambda.dati; public class Citta { private String nome; private boolean capoluogo; private boolean diMare; public Citta(String nome, boolean capoluogo, boolean diMare) { this.nome = nome; this.capoluogo = capoluogo; this.diMare = diMare; } public boolean isDiMare() { return diMare; } public void setDiMare(boolean diMare) { this.diMare = diMare; } public boolean isCapoluogo() { return capoluogo; } public void setCapoluogo(boolean capoluogo) { this.capoluogo = capoluogo; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } @Override public String toString() { return getNome(); } }
Il listato della classeEsercizio13C
invece, potrebbe essere codificato nel seguente modo:
package com.claudiodesio.lambda.test; import com.claudiodesio.lambda.dati.Citta; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; public class Esercizio13C { public static void main(String args[]) { List<Citta> listaCitta = getCitta(); System.out.println("Città di mare: " + filtraCitta(listaCitta, Esercizio13C::isDiMare)); listaCitta = getCitta(); System.out.println("Città capoluogo: " + filtraCitta(listaCitta, Esercizio13C::isCapoluogo)); listaCitta = getCitta(); } public static List<Citta> filtraCitta(List<Citta> listaCitta, Predicate<Citta> p) { final Iterator<Citta> iterator = listaCitta.iterator(); while (iterator.hasNext()) { Citta citta = iterator.next(); if (!p.test(citta)) { iterator.remove(); } } return listaCitta; } private static List<Citta> getCitta() { List<Citta> citta = new ArrayList<>(); citta.add(new Citta("Milano", true, false)); citta.add(new Citta("Rovigo", false, false)); citta.add(new Citta("Potenza", true, false)); citta.add(new Citta("Siracusa", false, true)); citta.add(new Citta("Perugia", true, false)); citta.add(new Citta("Napoli", true, true)); citta.add(new Citta("Pescara", false, true)); citta.add(new Citta("Taranto", false, true)); citta.add(new Citta("Siena", false, false)); return citta; } public static boolean isDiMare(Citta citta) { return citta.isDiMare(); } public static boolean isCapoluogo(Citta citta) { return citta.isCapoluogo(); } }
L'output sarà:
Città di mare: [Siracusa, Napoli, Pescara, Taranto] Città capoluogo: [Milano, Potenza, Perugia, Napoli]
-
Esercizio 13.d)
Dopo aver svolto l'esercizio precedente, creare una classe
Esercizio13D
alternativa aEsercizio13C
, che esegue le stesse operazioni usando le espressioni lambda.
Soluzione
Il listato della classe
Esercizio13D
potrebbe essere il seguente:
package com.claudiodesio.lambda.test; import com.claudiodesio.lambda.dati.Citta; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.function.Predicate; public class Esercizio13D { public static void main(String args[]) { List<Citta> listCitta = getCitta(); System.out.println("Città di mare: " + filtraCitta(listCitta, (citta) -> citta.isDiMare())); listCitta = getCitta(); System.out.println("Città capoluogo: " + filtraCitta(listCitta, (citta) -> citta.isCapoluogo())); } public static List<Citta> filtraCitta(List<Citta> listaCitta, Predicate<Citta> p) { final Iterator<Citta> iterator = listaCitta.iterator(); while (iterator.hasNext()) { Citta citta = iterator.next(); if (!p.test(citta)) { iterator.remove(); } } return listaCitta; } private static List<Citta> getCitta() { List<Citta> citta = new ArrayList<>(); citta.add(new Citta("Milano", true, false)); citta.add(new Citta("Rovigo", false, false)); citta.add(new Citta("Potenza", true, false)); citta.add(new Citta("Siracusa", false, true)); citta.add(new Citta("Perugia", true, false)); citta.add(new Citta("Napoli", true, true)); citta.add(new Citta("Pescara", false, true)); citta.add(new Citta("Taranto", false, true)); citta.add(new Citta("Siena", false, false)); return citta; } }
L'output rimarrà lo stesso dell'esercizio precedente:
Città di mare: [Siracusa, Napoli, Pescara, Taranto] Città capoluogo: [Milano, Potenza, Perugia, Napoli]
-
Esercizio 13.e)
Creare la classe
Esercizio13E
modificando la classeEsercizio13C
aggiungendo un metodostampaDettagli
che stampi la lista delle città, anche con le informazioni definite dalle variabilicapoluogo
ediMare
. Per esempio l'output potrebbe essere simile al seguente:
Città di mare: [Siracusa, Napoli, Pescara, Taranto] Città capoluogo: [Milano, Potenza, Perugia, Napoli]
Per ottenere tale risultato, non bisogna cambiare la classe
Citta
, ma usare un'espressione lambda inEsercizio13E
, sfruttando una delle interfacce funzionali standard, quale?
Soluzione
La classe
Esercizio13E
potrebbe essere la seguente:
package com.claudiodesio.lambda.test; import com.claudiodesio.lambda.dati.Citta; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; public class Esercizio13E { public static void main(String args[]) { List<Citta> listaCitta = getCitta(); System.out.println("Città di mare: " + filtraCitta(listaCitta, Esercizio13E::isDiMare)); listaCitta = getCitta(); System.out.println("Città capoluogo: " + filtraCitta(listaCitta, Esercizio13E::isCapoluogo)); listaCitta = getCitta(); stampaDettagli(listaCitta, (citta) -> System.out.println(citta.getNome() + (citta.isCapoluogo() ? " è capoluogo," : "") + (citta.isDiMare() ? " è città di mare," : ""))); } public static List<Citta> filtraCitta(List<Citta> listaCitta, Predicate<Citta> p) { final Iterator<Citta> iterator = listaCitta.iterator(); while (iterator.hasNext()) { Citta citta = iterator.next(); if (!p.test(citta)) { iterator.remove(); } } return listaCitta; } public static void stampaDettagli(List<Citta> listaCitta, Consumer<Citta> p) { for (Citta citta : listaCitta) { p.accept(citta); } } private static List<Citta> getCitta() { List<Citta> citta = new ArrayList<>(); citta.add(new Citta("Milano", true, false)); citta.add(new Citta("Rovigo", false, false)); citta.add(new Citta("Potenza", true, false)); citta.add(new Citta("Siracusa", false, true)); citta.add(new Citta("Perugia", true, false)); citta.add(new Citta("Napoli", true, true)); citta.add(new Citta("Pescara", false, true)); citta.add(new Citta("Taranto", false, true)); citta.add(new Citta("Siena", false, false)); return citta; } public static boolean isDiMare(Citta citta) { return citta.isDiMare(); } public static boolean isCapoluogo(Citta citta) { return citta.isCapoluogo(); } }
E quindi l'interfaccia funzionale da usare era l'interfacciaConsumer
.
È bastato poi invocare questo metodo usando un'espressione lambda (ed un paio di operatori ternari) nel metodomain
:
stampaDettagli(listaCitta, (citta) -> System.out.println(citta.getNome() + (citta.isCapoluogo() ? " è capoluogo, " : "") + (citta.isDiMare() ? " è città di mare" : "")));
L'output sarà:
Città di mare: [Siracusa, Napoli, Pescara, Taranto] Città capoluogo: [Milano, Potenza, Perugia, Napoli] Milano è capoluogo, Rovigo Potenza è capoluogo, Siracusa è città di mare, Perugia è capoluogo, Napoli è capoluogo, è città di mare, Pescara è città di mare, Taranto è città di mare, Siena
-
Esercizio 13.f)
Supponiamo di voler scrivere un programma e di voler riutilizzare la seguente classe
Persona
, ereditata da un programma già scritto e non modificabile:
public class Persona { private String nome; private String cognome; private String dataDiNascita; private String professione; private String indirizzo; public Persona(String nome, String cognome) { this.nome = nome; this.cognome = cognome; } public Persona(String nome, String cognome, String dataDiNascita, String professione, String indirizzo) { this.nome = nome; this.cognome = cognome; this.dataDiNascita = dataDiNascita; this.professione = professione; this.indirizzo = indirizzo; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getCognome() { return cognome; } public void setCognome(String cognome) { this.cognome = cognome; } public String getDataDiNascita() { return dataDiNascita; } public void setDataDiNascita(String dataDiNascita) { this.dataDiNascita = dataDiNascita; } public String getProfessione() { return professione; } public void setProfessione(String professione) { this.professione = professione; } public String getIndirizzo() { return indirizzo; } public void setIndirizzo(String indirizzo) { this.indirizzo = indirizzo; } @Override public String toString() { return "Persona{" + "nome=" + nome + ", cognome=" + cognome + '}'; } }
Purtroppo nel nostro contesto, avremmo bisogno di ridefinire il metodo
toString
in modo che stampi non solo le informazioni sul nome e il cognome della persona, ma anche la data di nascita, l'indirizzo e la professione. Come già detto però, la classe è già in uso e non è possibile modificarla. In particolare il nostro requisito è che il metodotoString
Nome: Arjen Anthony Cognome: Lucassen Professione: Compositore Data di Nascita: 03/04/1960 Indirizzo: Olanda
Si crei quindi una classe
TestPersona
che ridefinisca in qualche modo il metodotoString
della classePersona
e stampi l'output di cui sopra.
Per la formattazione è possibile sfruttare il carattere di escape\t
introdotto nel capitolo 3.
Soluzione
La soluzione è molto semplice utilizzando una classe anonima al volo come nel seguente listato, la difficoltà semmai è nella formattazione del codice sfruttando tabulazioni (
\t
):
public class TestPersona { public static void main(String args[]) { System.out.println(new Persona("Arjen Anthony", "Lucassen", "03/04/1960", "Compositore", "Olanda") { @Override public String toString() { String string = "Nome: \t\t\t" + getNome(); string += "\nCognome: \t\t" + getCognome(); string += "\nProfessione: \t\t" + getProfessione(); string += "\nData di Nascita: \t" + getDataDiNascita(); string += "\nIndirizzo: \t\t" + getIndirizzo(); return string; } }); } }
-
Esercizio 13.g)
Consideriamo la classe
Osservazione
della soluzione dell'esercizio 12.c, che riportiamo di seguito per comodità:
package com.claudiodesio.osservatorio.test; import com.claudiodesio.osservatorio.dati.Partecipante; import com.claudiodesio.osservatorio.dati.Telescopio; public class Osservazione { public static void main(String args[]) { Telescopio telescopio = new Telescopio(); Partecipante[] partecipanti = getPartecipanti(telescopio); for (Partecipante partecipante : partecipanti) { partecipante.start(); } } private static Partecipante[] getPartecipanti(Telescopio telescopio) { Partecipante[] partecipanti = { new Partecipante("Ciro", telescopio), new Partecipante("Gianluca", telescopio), new Partecipante("Pierluigi", telescopio), new Partecipante("Gigi", telescopio), new Partecipante("Nicola", telescopio), new Partecipante("Pino", telescopio), new Partecipante("Maurizio", telescopio), new Partecipante("Raffaele", telescopio), new Partecipante("Fabio", telescopio), new Partecipante("Vincenzo", telescopio)}; return partecipanti; } }
È possibile usare un'espressione lambda per implementare un override del metodo
run
della classePartecipante
, per uno dei partecipanti?
Soluzione
Anche in questo caso, è impossibile usare un'espressione lambda. Invece è possibile utilizzare una classe anonima:
package com.claudiodesio.osservatorio.test; import com.claudiodesio.osservatorio.dati.Partecipante; import com.claudiodesio.osservatorio.dati.Telescopio; public class Osservazione { public static void main(String args[]) { Telescopio telescopio = new Telescopio(); Partecipante[] partecipanti = getPartecipanti(telescopio); for (Partecipante partecipante : partecipanti) { partecipante.start(); } } private static Partecipante[] getPartecipanti(Telescopio telescopio) { Partecipante[] partecipanti = { new Partecipante("Ciro", telescopio), new Partecipante("Gianluca", telescopio), new Partecipante("Pierluigi", telescopio), new Partecipante("Gigi", telescopio), new Partecipante("Nicola", telescopio) { @Override public void run() { System.out.println(getNome() + " sono pronto!"); super.run(); } }, new Partecipante("Pino", telescopio), new Partecipante("Maurizio", telescopio), new Partecipante("Raffaele", telescopio), new Partecipante("Fabio", telescopio), new Partecipante("Vincenzo", telescopio)}; return partecipanti; } }
-
Esercizio 13.h)
Considerato il seguente codice:
new Thread(()->System.out.print("Java"); System.out.print("Java");).start();
quali delle seguenti affermazioni sono corrette?
1. Eseguendo questo snippet con JShell viene stampata la stringaJavaJava
.
2. Eseguendo questo snippet con JShell viene stampata la stringaJava
.
3. Questo snippet non supererà la compilazione.
4. Questo snippet non lancerà un'eccezione al runtime.
5. Questo snippet produrrà un warning in compilazione.
Soluzione
Solo la terza affermazione è corretta. L'output di JShell è il seguente:
| Error: | ')' expected | new Thread(()->System.out.print("Java");System.out.print("Java");).start(); |
Infatti mancano le parentesi graffe intorno al blocco di codice. Se ci fossero:
new Thread(()->{System.out.print("Java"); System.out.print("Java");}).start();
allora la prima affermazione sarebbe stata quella corretta:
jshell> JavaJava
-
Esercizio 13.i)
Considerato il seguente codice:
new Thread((int a)->{ int b = 0; b = a/b; }).start();
quali delle seguenti affermazioni sono corrette?
1. Il codice non compila perché bisogna gestire unaArithmeticException
.
2. Il codice compila e viene eseguito correttamente.
3. Il codice compila ma lancerà unaArithmeticException
durante l'esecuzione.
4. Il codice produrrà un warning in compilazione.
Soluzione
Nessuna delle affermazioni è corretta. Infatti il codice non compilerà perché al costruttore di un
Thread
bisogna passare un'implementazione del metodorun
, che non prende in input parametri come specificato nel codice. Eseguendo questo snippet su JShell otterremo il seguente output:
jshell> new Thread((int a)->{ ...> int b = 0; ...> b = a/b; ...> }).start(); | Error: | no suitable constructor found for Thread((int a)->{[...] b; }) | constructor java.lang.Thread.Thread(java.lang.Runnable) is not applicable | (argument mismatch; incompatible parameter types in lambda expression) | constructor java.lang.Thread.Thread(java.lang.String) is not applicable | (argument mismatch; java.lang.String is not a functional interface) | new Thread((int a)->{ | ^--------------------...
Notiamo cheArithmeticException
estendeRuntimeException
non una checked exception. Il compilatore quindi non avrebbe segnalato errori, e per questa ragione la prima affermazione era sicuramente scorretta. La seconda e la quarta affermazione sono ovviamente false. La terza affermazione sarebbe stata corretta se l'implementazione del metodo run fosse stata corretta, ma non lo è. -
Esercizio 13.j) Tipi innestati, Vero o Falso:
1. Una classe innestata è una classe che viene dichiarata all'interno di un'altra classe.
2. Una classe anonima è anche innestata, ma non ha nome. Inoltre, per essere dichiarata, deve per forza essere istanziata.
3. Le classi innestate non sono necessarie per l'Object Orientation.
4. Una classe innestata deve essere per forza istanziata.
5. Per istanziare una classe innestata pubblica a volte bisogna istanziare prima la classe esterna.
6. Una classe innestata dichiarataprivate
deve dichiarare anche i metodi "set" e "get" per poter essere utilizzata da una terza classe.
7. Una classe innestata non può avere lo stesso nome della classe che la contiene.
8. Una classe anonima può avere lo stesso nome della classe che la contiene.
9. Una classe innestata può accedere a membri statici della classe che la contiene solo se è dichiarata statica.
10. Una classe innestata non può essere dichiarata astratta.
Soluzione
1. Vero.
2. Vero.
3. Vero.
4. Falso, le classi anonime devono per forza essere istanziate.
5. Vero, ma non l'abbiamo visto all'interno del libro.
6. Falso.
7. Vero.
8. Falso, una classe anonima non ha nome.
9. Vero.
10. Falso, una classe anonima non può essere dichiarata astratta. -
Esercizio 13.k)
Data la seguente classe:
public class Esterna { public int a = 1; private int b = 2; public void metodoEsterno(final int c) { int d = 4; class Interna { private void metodoInterno(int e) { } } } }
Quali delle seguenti affermazioni sono corrette?1. All'interno del metodometodoInterno
è possibile referenziare la variabilea
.
2. All'interno del metodometodoInterno
è possibile referenziare la variabileb
.
3. All'interno del metodometodoInterno
è possibile referenziare la variabilec
.
4. All'interno del metodometodoInterno
è possibile referenziare la variabiled
.
5. All'interno del metodometodoInterno
è possibile referenziare la variabilee
.
6. La classe non può essere compilata.
Soluzione
Le affermazioni corrette sono le 1, 2, 3, 4 e 5.
-
Esercizio 13.l)
Supponiamo di avere un'espressione lambda che usa un metodo che lancia una checked exception senza gestirla. Quali delle seguenti affermazioni è corretta?
1. Il codice non compila perché bisogna gestire una checked exception nel blocco di codice dell'espressione lambda.
2. Il codice non compila, ma se è possibile, possiamo renderlo compilabile ridefinendo il metodo SAM dell'interfaccia funzionale gestendo l'eccezione all'interno del metodo stesso.
3. Il codice compila tranquillamente.
4. Dal momento che dobbiamo gestire l'eccezione, perdiamo le capacità di deduzione del compilatore, che ci porta a scrivere meno codice.
Soluzione
Le affermazioni sono tutte corrette tranne la numero 3.
-
Esercizio 13.m)
Creare l'interfaccia funzionale che possa soddisfare il seguente uso di espressioni lambda:
Operazione operazione1 = (double a, double b) -> a + b; Operazione operazione2 = (double a, double b) -> a - b; Operazione operazione3 = (double a, double b) -> a / b; Operazione operazione4 = (double a, double b) -> a * b;
Soluzione
La soluzione è:
public interface Operazione { double operazione(double x, double y); }
-
Esercizio 13.n)
Esiste un'interfaccia funzionale standard, capace di sostituire l'interfaccia funzionale
Operazione
dell'esercizio precedente?Usare la documentazione per risolvere l'esercizio.
Soluzione
Sì, esiste la classe
DoubleBinaryOperator
che definisce il metodo:
double applyAsDouble(double left, double right)
che coincide come parametri e tipo di ritorno con il metodo operazione dell'interfacciaOperazione
dell'esercizio precedente (ovviamente il nome non conta). -
Esercizio 13.o)
Riscrivere la soluzione dell'esercizio 13.b, sostituendo le espressioni lambda con reference a metodi.
Soluzione
La soluzione potrebbe essere la seguente:
package com.claudiodesio.lambda.test; import java.util.Arrays; import java.util.Comparator; public class Esercizio13O { static String nomi[] = {"Clarissa","Jem","Top","Ermeringildo","Iamaca", "Tom","Arlequin","Francesca","Cumbus","Blue" } ; static int compareLunghezza(String first, String second) { return -(Integer.compare(first.length(), second.length())); } static int compareLunghezzaAlContrario(String first, String second) { return (Integer.compare(first.length(), second.length())); } static int compareAlfabeticoAlContrario(String first, String second) { return -(first.compareTo(second)); } static int compareLunghezzaEAlfabeticoAlContrario(String first, String second) { int result = -Integer.compare(first.length(), second.length()); if (result == 0) { result = first.compareTo(second); } return result; } public static void main(String args[]) { Arrays.sort(nomi, Esercizio13O::compareLunghezza); System.out.println("Nomi ordinati per lunghezza: " + Arrays.asList(nomi)); Arrays.sort(nomi, Esercizio13O::compareLunghezzaAlContrario); System.out.println("Nomi ordinati per lunghezza al contrario: " + Arrays.asList(nomi)); Arrays.sort(nomi, String::compareTo); System.out.println("Nomi ordinati : " + Arrays.asList(nomi)); Arrays.sort(nomi, Esercizio13O::compareAlfabeticoAlContrario); System.out.println("Nomi ordinati al contrario: " + Arrays.asList(nomi)); Arrays.sort(nomi, Esercizio13O::compareLunghezzaEAlfabeticoAlContrario); System.out.println( "Nomi ordinati per lunghezza al contrario e in ordine alfabetico: " + Arrays.asList(nomi)); } }
-
Esercizio 13.p)
Come si chiama l'interfaccia funzionale che si adatta meglio a fungere da "Factory"?
1.Predicate
2.Factory
3.Function
4.Supplier
5.Consumer
Soluzione
L'esercizio era davvero semplice, la risposta giusta è la numero 4, Supplier, in quanto la definizione del suo metodo è la seguente:
dove T è un parametro generico dell'interfaccia. Si noti che l'interfaccia funzionale Factory della risposta 2 non esiste.T get()
-
Esercizio 13.q)
Selezionare le affermazioni corrette.
Un'interfaccia funzionale:
1. Deve essere annotata con l'annotazioneFunctionalInterface
.
2. Deve dichiarare un unico metodo.
3. Deve essere per forza implementata.
4. Non può essere estesa da un'altra interfaccia funzionale.
5. Non può essere estesa da un'altra interfaccia.
Soluzione
Nessuna delle affermazioni è corretta.
Solamente la numero 2 potrebbe non risultare scorretta in maniera evidente. Infatti un'interfaccia funzionale deve dichiarare un unico metodo astratto, ma è possibile dichiarare anche metodi di default e statici, sia pubblici che privati. -
Esercizio 13.r)
Quali delle seguenti affermazioni sono corrette riguardo i reference a costruttore?
1. La sintassi èNomeClasse::New
.
2. La sintassi èNomeClasse::new()
.
3. La sintassi èNomeClasse::new
.
4. Un reference a un costruttore si può assegnare ad un reference di un'interfaccia funzionale, il cui metodo SAM ritornavoid
.
5. Con un reference ad un costruttore, possiamo sostituire l'implementazione di un'interfaccia funzionale.
Soluzione
Le affermazioni corrette sono la numero 3 (il che esclude la correttezza della 1 e della 2) e la numero 5 (che è coerente con la definizione delle espressioni lambda). L'affermazione numero 4 è scorretta perché il metodo SAM non può restituire void, ma deve restituire lo stesso tipo del costruttore.
-
Esercizio 13.s)
Creare una classe
Persona
che dichiari le variabilinome
eanni
(nel senso di anni di età) e che implementi l'interfacciaComparable
in modo tale che il metodo ereditatocompareTo
ordini per età crescente, e, nel caso di persone della stessa età, in ordine alfabetico (considerando il nome). Si faccia override anche del metodotoString
.
Poi, considerata la seguente classe:
import java.util.Arrays; public class Esercizio13S { public static void main(String args[]) { Persona [] persone = { new Persona("Antonio",21), new Persona("Bruno",20), new Persona("Giorgio",19), new Persona("Martino",22), new Persona("Daniele",21) }; Arrays.sort(persone, /*INSERISCI IL CODICE QUI*/); System.out.println(Arrays.toString(persone)); } }
si inserisca il codice corretto al posto del commento/*INSERISCI IL CODICE QUI*/
, in modo tale da generare il seguente output:
[Giorgio, Bruno, Antonio, Daniele, Martino]
Soluzione
La classe
Persona
potrebbe essere la seguente:
import java.util.*; public class Persona implements Comparable<Persona> { private String nome; private int anni; public Persona(String nome, int anni) { this.nome = nome; this.anni = anni; } public void setNome(String nome) { this.nome = nome; } public String getNome() { return nome; } public void setAnni(int anni) { this.anni = anni; } public int getAnni() { return anni; } @Override public int compareTo(Persona altraPersona) { int result = Integer.valueOf(this.anni).compareTo( Integer.valueOf(altraPersona.anni)); if (result == 0) { result = this.nome.compareTo(altraPersona.nome); } return result; } public String toString() { return nome; } }
Invece la classeEsercizio13S
può essere completata con il "reference a metodo d'istanza di un certo tipo" nel seguente modo:
import java.util.Arrays; public class Esercizio13S { public static void main(String args[]) { Persona [] persone = { new Persona("Antonio",21), new Persona("Bruno",20), new Persona("Giorgio",19), new Persona("Martino",22), new Persona("Daniele",21) }; Arrays.sort(persone, Persona::compareTo); System.out.println(Arrays.toString(persone)); } }
-
Esercizio 13.t)
Quale interfaccia funzionale potrebbe essere utilizzata al posto del seguente metodo?
boolean haQuestoNome(Persona persona, String nome) { return nome.equals(persona.getNome()); }
Soluzione
La risposta giusta è la numero 6:BiPredicate
. Infatti essa definisce il metodo:
boolean test(T t, U u)
-
Esercizio 13.u)
Partendo dalla classe
Persona
dell'esercizio 13.s, e considerando la seguente classe:
import java.util.Arrays; import java.util.function.BiPredicate; public class Esercizio13U { public static void main(String args[]) { Persona [] persone = { new Persona("Antonio",21), new Persona("Bruno",20), new Persona("Giorgio",19), new Persona("Martino",22), new Persona("Daniele",21) }; Persona personaCheIniziaPerD = getPersonaCheIniziaPer("D", persone, /*INSERISCI CODICE QUI*/); System.out.println(personaCheIniziaPerD); } static Persona getPersonaCheIniziaPer(String iniziale, Persona[] persone, BiPredicate<String, Persona> biPredicate) { for(Persona persona : persone) { if (biPredicate.test(iniziale, persona)) { return persona; } } return null; } }
Quale espressione lambda è possibile inserire al posto del commento
/*INSERISCI CODICE QUI*/
per recuperare il primo oggettoPersona
dell'array persone che ha un nome che inizia con la "D"?
Soluzione
La soluzione potrebbe essere la seguente:
import java.util.Arrays; import java.util.function.BiPredicate; public class Esercizio13U { public static void main(String args[]) { Persona [] persone = { new Persona("Antonio",21), new Persona("Bruno",20), new Persona("Giorgio",19), new Persona("Martino",22), new Persona("Daniele",21) }; Persona personaCheIniziaPerD = getPersonaCheIniziaPer("D", persone, (String iniziale, Persona persona) -> persona.getNome().startsWith(iniziale)); System.out.println(personaCheIniziaPerD); } static Persona getPersonaCheIniziaPer(String iniziale, Persona[] persone, BiPredicate<String, Persona> biPredicate){ for(Persona persona : persone) { if (biPredicate.test(iniziale, persona)) { return persona; } } return null; } }
-
Esercizio 13.v)
Quali delle seguenti affermazioni sono corrette?
1. Con le espressioni lambda il referencethis
si riferisce direttamente alla classe in cui è inclusa l'espressione.
2. Per le espressioni lambda valgono le stesse regole che valgono per le classi anonime.
3. La sintassi di un'espressione lambda permette di utilizzare altre espressioni lambda innestate.
4. La sintassi di un'espressione lambda permette di utilizzare reference a metodi innestati.
5. La sintassi di un reference a metodo permette di utilizzare altre espressioni lambda innestate.
6. La sintassi di un reference a metodo permette di utilizzare reference a metodi innestati.
Soluzione
Le risposte corrette sono la numero 1, 3 e 4. La numero 2 è falsa, anzi il fatto che le espressioni lambda non ereditino le complicate regole delle classi anonime è uno dei vantaggi dell'utilizzare espressioni lambda al posto delle classi anonime. Le numero 5 e 6 sono false perché la sintassi di un reference a metodo non prevede codice innestato, e quindi è impossibile utilizzare altre espressioni lambda innestate o altri reference a metodo.
-
Esercizio 13.w)
Ripetere l'esercizio 13.f, utilizzando una espressione lambda in luogo della classe anonima. Riportiamo il testo dell'esercizio 13.f per comodità qui di seguito. Supponiamo di voler scrivere un programma e di voler riutilizzare la seguente classe Persona, ereditata da un programma già scritto e non modificabile:
public class Persona { private String nome; private String cognome; private String dataDiNascita; private String professione; private String indirizzo; public Persona(String nome, String cognome) { this.nome = nome; this.cognome = cognome; } public Persona(String nome, String cognome, String dataDiNascita, String professione, String indirizzo) { this.nome = nome; this.cognome = cognome; this.dataDiNascita = dataDiNascita; this.professione = professione; this.indirizzo = indirizzo; } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getCognome() { return cognome; } public void setCognome(String cognome) { this.cognome = cognome; } public String getDataDiNascita() { return dataDiNascita; } public void setDataDiNascita(String dataDiNascita) { this.dataDiNascita = dataDiNascita; } public String getProfessione() { return professione; } public void setProfessione(String professione) { this.professione = professione; } public String getIndirizzo() { return indirizzo; } public void setIndirizzo(String indirizzo) { this.indirizzo = indirizzo; } @Override public String toString() { return "Persona{nome=" + nome + ", cognome=" + cognome + "}"; } }
Purtroppo nel nostro contesto, avremmo bisogno di ridefinire il metodotoString
in modo che stampi non solo le informazioni sul nome e il cognome della persona, ma anche la data di nascita, l'indirizzo e la professione. Come già detto però, la classe è già in uso e non è possibile modificarla. In particolare il nostro requisito è che il metodotoString
restituisca la seguente stringa:
Nome: Arjen Anthony Cognome: Lucassen Professione: Compositore Data di Nascita: 03/04/1960 Indirizzo: Olanda
Creare quindi una classe
TestPersona
che ridefinisca in qualche modo il metodotoString
della classePersona
e stampi l'output di cui sopra.
Soluzione
È impossibile in questo caso sostituire la classe anonima con un'espressione lambda. Ricordiamo che un'espressione lambda funziona implementando interfacce funzionali (con un solo metodo astratto).
-
Esercizio 13.x)
Data la seguente classe:
public class Esterna { private int intero = 5; public static void main(String[] args) { Esterna.Interna interna = new Esterna().new Interna(); interna.metodoInterno(); } private class Interna { private int intero = 10; protected void metodoInterno() { System.out.println(super.intero); } } }
Quali delle seguenti affermazioni sono vere?
1. Eseguita la classeEsterna
, viene lanciata unaNullPointerException
.
2. Eseguita la classeEsterna
, viene stampato50
.
3. Eseguita la classeEsterna
, viene stampato10
.
4. Eseguita la classeInterna
, viene stampato5
.
5. Eseguita la classeInterna
, viene stampato0
.
6. Impossibile compilare questo codice.
Soluzione
La risposta corretta è l'ultima. Infatti l'espressione
super.intero
, implicherebbe che la superclasse della classeInterna
(ovvero la classeObject
) abbia una variabile che si chiamaintero
, cosa che in realtà non sussiste.
Segue il messaggio di errore della compilazione:
Esterna.java:11: error: cannot find symbol System.out.println(super.intero); symbol: variable intero 1 error
-
Esercizio 13.y)
Tenendo conto della risposta dell'esercizio precedente, modificare il codice (con una modifica minimale) in modo tale da far stampare il valore 5. Poi modificarlo nuovamente per fargli stampare anche il valore
10
.
Soluzione
La soluzione che appare più semplice è la seguente:
public class Esterna { private int intero = 5; public static void main(String[] args) { Esterna.Interna interna = new Esterna().new Interna(); interna.metodoInterno(); } private class Interna extends Esterna { private int intero = 10; protected void metodoInterno() { System.out.println(super.intero); System.out.println(this.intero); } } }
-
Esercizio 13.z)
Quali delle seguenti affermazioni sono corrette?
1. L'interfaccia funzionale che dichiara due tipi generici come parametri in input e ne ritorna un altro, si chiamaBiFunction
.
2. L'interfaccia funzionale che non dichiara tipi generici come parametri in input, ma ne ritorna un altro, si chiamaProvider
.
3. L'interfaccia funzionale che non dichiara tipi generici come parametri in input, ma ne ritorna un altro, si chiamaSupplier
.
4. L'interfaccia funzionale che dichiara un tipo generico come parametro in input e ne ritorna un altro, si chiamaFunction
.
5. L’interfaccia funzionale che dichiara tre tipi generici come parametri in input e ne ritorna un altro, si chiamaTriFunction
.
6. L’interfaccia funzionale che dichiara un tipo generico come parametro in input e ritorna lo stesso tipo, si chiamaUnaryOperator
.
7. È possibile concatenare piùUnaryOperator
tramite il metodo di defaultand
.
8. L’interfaccia funzionale che dichiara un tipo generico come parametro in input e non ritorna nulla, si chiamaConsumer
.
Soluzione
Le risposte corrette sono la numero 1, 3, 4, 6 e 8. La numero 2 è falsa visto che è corretta la numero 3. La numero 5 è falsa perché
TriFunction
non esiste, ma si potrebbe creare facilmente. La numero 7 è falsa perché non esiste il metodoand
all'interno diUnaryOperator
(semmai esisteandThen
). -
Esercizio 13.aa)
Data la seguente classe:
public class Esercizio13AA { public static void main(String args[]) { new Interfaccia() { public void metodo() { System.out.println("Classe anonima"); } }.metodo(); } private interface Interfaccia { void metodo(); } }
Quali delle seguenti affermazioni è vera?
1. Eseguita la classeEsercizio13AA
, viene lanciata unaNullPointerException
.
2. Eseguita la classeEsercizio13AA
, viene lanciata unaCannotInstantiateInterfaceException
.
3. Eseguita la classeEsercizio13AA
, viene stampato "Classe Anonima".
4. Eseguita la classeEsercizio13AA
, viene stampato "Null".
5. Impossibile compilare questo codice.
Soluzione
L'affermazione corretta è la 3. Infatti il codice del metodo main istanzia una classe anonima al volo, ridefinisce il metodo
metodo
e, senza assegnare la nuova istanza ad un reference, chiama (sempre al volo) il metodo appena ridefinito.Sarebbe stato opportuno anche annotare il metodo ridefinito con l'annotazioneOverride
. -
Esercizio 13.bb)
Data la seguente classe:
public class Esterna { private class Interna { private static int effectivelyFinalVariable = 10; Interna() { effectivelyFinalVariable = 11; } protected void metodo () { System.out.println(effectivelyFinalVariable); } } }
Quali delle seguenti affermazioni sono vere?
1. Impossibile compilare questo codice.
2. La variabileeffectivelyFinalVariable
non è "effettivamentefinal
".
3. La variabileeffectivelyFinalVariable
non ha importanza che sia o meno "effettivamentefinal
" perché è una variabile d'istanza.
4. La variabileeffectivelyFinalVariable
non ha importanza che sia o meno "effettivamentefinal
" perché appartiene alla classe interna e non alla classe esterna.
Soluzione
Tutte le affermazioni sono vere. In particolare la numero 1 è vera, perché "Una classe innestata può dichiarare membri statici solo se dichiarata statica.".
-
Esercizio 13.cc)
Data la seguente classe:
public class Esterna { private final static String stringa = "Classe Innestata"; protected Esterna() { private static class Innestata { protected void metodo() { System.out.println(stringa); } } } }
Quali delle seguenti affermazioni sono vere?
1. Impossibile compilare questo codice.
2. La costante statica stringa non è "effettivamentefinal
" perché è anche statica, e quindi non può essere utilizzata all'interno della classe internaInnestata
.
3. La costante statica stringa non è accessibile al metodometodo
perché dichiarata statica.
4. La classeInterna
, essendo dichiarata all'interno di un costruttoreprotected
, può essere utilizzata solo all'interno del costruttore.
Soluzione
L'affermazione 1 è corretta. Infatti non è possibile compilare questo codice, perché non è possibile dichiarare una classe innestata all'interno di un costruttore (o di un metodo).
La numero 2 è falsa perché, mentre la prima affermazione potrebbe considerarsi corretta (se è dichiarata statica una variabile non può essere locale, e solo le variabili locali possono essere effettivamentefinal
), la seconda non è vera perché la classe innestataInnestata
, è a sua volta dichiarata statica, e quindi potrebbe utilizzare le variabili statiche della classeEsterna
.
Per lo stesso motivo la numero 3 è anch'essa falsa.
La numero 4 è ovviamente falsa perché, come abbiamo detto per l'affermazione 1, non è possibile dichiarare una classe innestata all'interno di un costruttore (o di un metodo). -
Esercizio 13.dd)
Dato questo codice:
public enum Esercizio13DD { A, B, C; public class Interna { public enum EnumInterna { D, E, F; } } public static void main(String args[]) { for (Esercizio13DD.Interna.EnumInterna item : Esercizio13DD.Interna.EnumInterna.values()) { System.out.println(item); } } }
Una volta eseguita l'enumerazione
Esercizio13DD
, l'output sarà:
1. A, B, C
2. D, E, F
3. A B C
4. D E F
5. ABC
6. DEF
7. Nessuna delle precedenti: viene lanciata unaNullPointerException
.
8. Nessuna delle precedenti: il codice non compila per un errore nel metodomain
.
9. Nessuna delle precedenti: il codice non compila per un errore nella classeInterna
.La risposta a questo esercizio dipende dalla versione di Java che stiamo utilizzando (vedi nota nella risposta).
Soluzione
La seguente risposta vale se stiamo utilizzando una versione di Java non superiore alla 15. Infatti, con l’introduzione ufficiale dei tipi record nella versione 16 le regole del compilatore sono cambiate. In pratica è stato reso possibile la definizione di membri statici all’interno delle classi interne (cfr. capitolo 21.1.2 del capitolo extra online denominato “Aggiornamento a Java 17”). Quindi se usiamo una versione di Java dalla 16 in poi, allora la risposta giusta sarà la numero 4.La risposta corretta è l'ultima (la numero 9). Infatti non è possibile dichiarare una enumerazione all'interno di una classe interna.
L'output della compilazione è il seguente:
Esercizio11S.java:4: error: enum declarations allowed only in static contexts public enum EnumInterna { ^ 1 error
-
Esercizio 13.ee)
(ALLERTA SPOILER: le prossime righe rivelano la soluzione del precedente esercizio).
Dopo aver letto la soluzione dell'esercizio precedente (in particolare l'output della compilazione), si modifichi il codice per renderlo compilabile, risolvendo i vari errori che si presentano dopo le varie compilazioni. Eseguendo la versione compilabile definitiva il programma dovrà stampare il seguente output:
D E F
Questo esercizio testa la vostra capacità di interpretare i messaggi di errore del compilatore, e risolverli. Praticamente quello che ogni programmatore fa in continuazione.
Soluzione
La seguente risposta vale se stiamo utilizzando una versione di Java non superiore alla 15. Infatti, con l’introduzione ufficiale dei tipi record nella versione 16 le regole del compilatore sono cambiate. In pratica è stato reso possibile la definizione di membri statici all’interno delle classi interne (cfr. capitolo 21.1.2 del capitolo extra online denominato “Aggiornamento a Java 17”). Quindi se usiamo una versione di Java dalla 16 in poi, questo esercizio è già stato risolto nel precedente.Interpretando l'output della soluzione precedente:
Esercizio11S.java:4: error: enum declarations allowed only in static contexts public enum EnumInterna { ^ 1 error
Si arriva facilmente a capire che il problema è che le classi interne (che ricordiamo essere classi innestate non statiche) non possono dichiarare enumerazioni. Il compilatore però ci dice che le dichiarazioni di enumerazioni sono permesse solo in contesti statici, così viene naturale dichiarare la classe innestata come statica in questo modo:
public enum Esercizio11T { A, B, C; public static class Interna { public enum EnumInterna { D, E, F; } } public static void main(String args[]) { for (Esercizio11S.Interna.EnumInterna item : Esercizio11S.Interna.EnumInterna.values()) { System.out.println(item); } } }
Compilando questo codice, otterremo il seguente output:
Esercizio11T.java:9: error: package Esercizio11T.Interna does not exist for (Esercizio11T.Interna.EnumInterna item : Esercizio11T.Interna.EnumInterna.values()) { ^ Esercizio11T.java:9: error: package Esercizio11S.Interna does not exist for (Esercizio11T.Interna.EnumInterna item : Esercizio11T. Interna.EnumInterna.values()) { ^ 2 errors
Il che ci fa capire che il tipoEsercizio11T.Interna.EnumInterna
non viene riconosciuto come tipo valido. La soluzione consiste nell'utilizzare il tipo corretto per il nostro contesto di visibilità:Interna.EnumInterna
(ovvero non abbiamo specificato l'enumerazione che contiene sia il metodomain
, sia la classeInterna
). Quindi il codice dovrebbe essere il seguente:
public enum Esercizio11T { A, B, C; public static class Interna { public enum EnumInterna { D, E, F; } } public static void main(String args[]) { for (Interna.EnumInterna item : Interna.EnumInterna.values()) { System.out.println(item); } } }
che verrà compilato senza errori, ed il cui output risulterà essere il seguente:
D E F
come richiesto. -
Esercizio 13.ff)
Cosa dobbiamo utilizzare per far eseguire algoritmi diversi (anche parziali) ad un metodo (scegliere tutte le risposte corrette) senza aver prima creato il codice dell'algoritmo?
1. Un'oggetto istanziato da una classe già esistente.
2. Un'interfaccia.
3. Un'enumerazione.
4. Una classe interna.
5. Una classe innestata.
6. Una classe anonima.
7. Un costruttoswitch
.
8. Niente, semplicemente non è possibile.
per tutti gli esercizi di questo libro, è possibile consultare la documentazione della libreria standard per trovare le soluzioni. Questo vale soprattutto nei capitoli come questi dedicati proprio alle librerie.
Soluzione
La risposta è la numero 6, come è possibile verificare nel paragrafo 13.1.7 quando nell'esempio della videoteca avevamo creato il codice da far eseguire al volo ad un metodo con la seguente sintassi:
public class TestVideoteca { public static void main(String args[]) { //... Videoteca videoteca = new Videoteca(); System.out.println("Bei Film:"); Film[] beiFilms = videoteca.getFilmFiltrati(new FiltroFilm() { @Override public boolean filtra(Film film) { return film.getMediaRecensioni() >3; } }); //... System.out.println("\nFilm di Fantascienza:"); Film[] filmDiFantascienza = videoteca.getFilmFiltrati( new FiltroFilm() { @Override public boolean filtra(Film film) { return"Fantascienza".equals(film.getGenere()); } }); //...
dove la classe
Videoteca
era così codificata:
public class Videoteca { private Film[] films; public Videoteca () { films = new Film[10]; caricaFilms(); } public void setFilms(Film[] films) { this.films = films; } public Film[] getFilms() { return films; } /* I SEGUENTI METODI SONO STATI SOSTITUITI DAL METODO DEFINITO SUBITO DOPO public Film[] getFilmDiFantascienza() { Film [] filmDiFantascienza = new Film[10]; for (int i = 0, j= 0; i< 10;i++) { if ("Fantascienza".equals(films[i].getGenere())) { filmDiFantascienza[j] = films[i]; j++; } } return filmDiFantascienza; } public Film[] getBeiFilm() { Film [] beiFilms = new Film[10]; for (int i = 0, j= 0; i< 10;i++) { if (films[i].getMediaRecensioni() > 3) { beiFilms[j] = films[i]; j++; } } return beiFilms; } */ /* QUESTO METODO SOSTITUISCE I DUE PRECEDENTI (COMMENTATI) */ public Film[] getFilmFiltrati(FiltroFilm filtroFilm) { Film [] filmFiltrati = new Film[10]; for (int i = 0, j= 0; i< 10;i++) { if (filtroFilm.filtra(films[i])) { filmFiltrati[j] = films[i]; j++; } } return filmFiltrati; } private void caricaFilms() { //Caricamento film... } }
e dove l'interfacciaFiltroFilm
, che viene ridefinita con le classi anonime nella classeTestVideoteca
, era semplicemente la seguente:
public interface FiltroFilm { boolean filtra(Film film); }
-
Esercizio F.a) Autoboxing, autounboxing e java.lang, Vero o Falso:
1. Il seguente codice compila senza errori:
2. Il seguente codice compila senza errori:char c = new String("Pippo");
int c = new Integer(1) + 1 + new Character('a');
3. Le regole dell'overload non cambiano con l'introduzione dell'autoboxing e dell'autounboxing.
4. Le istanze della classeInteger
sono immutabili, e quindi non è possibile mutare il loro stato interno una volta istanziate.
5. La classeRuntime
dipende strettamente dal sistema operativo su cui gira.
6. La classeClass
permette di leggere i membri di una classe (ma anche le superclassi ed altre informazioni) partendo semplicemente dal nome della classe grazie al metodoforName
.
7. Tramite la classeClass
è possibile istanziare oggetti di una certa classe conoscendone solo il nome.
8. È possibile dalla versione 1.4 di Java sommare un tipo primitivo e un oggetto della relativa classe wrapper, come nel seguente esempio:Integer a = new Integer(30); int b = 1; int c = a+b;
9. La clonazione di oggetti richiede obbligatoriamente una chiamata al metodo
clone
diObject
.
10. La classeMath
non si può istanziare perché dichiarataabstract
.
Soluzione
1. Falso.
2. Vero.
3. Vero.
4. Vero.
5. Vero.
6. Vero.
7. Vero.
8. Falso, dalla versione 1.5.
9. Falso.
10. Falso, non si può istanziare perché ha un costruttore privato ed è dichiaratafinal
per non poter essere estesa. -
Esercizio F.b)
Quali delle seguenti affermazioni sono corrette?
1. Un oggetto immutabile non può essere modificato.
2. Le stringhe sono oggetti immutabili.
3. Le istanze delle classi wrapper sono oggetti immutabili.
4. Un oggetto immutabile può essere puntato da più reference.
5. Un oggetto immutabile non permette di modificare il suo stato interno.
6. Un oggetto immutabile non permette di modificare il suo stato esterno.
Soluzione
Tutte le affermazioni sono corrette. In particolare nell'ultima si parla di modificare lo stato esterno di un oggetto… ma in realtà non esiste uno "stato esterno" da modificare.
-
Esercizio F.c)
Quali tra le seguenti affermazioni sono corrette rispetto all'argomento delle compact strings?
1. È possibile specificare quali stringhe devono essere rese compatte.
2. Una stringa non compatta utilizza 16 bit.
3. Una stringa compatta utilizza solo 8 bit.
4. Per rendere in un programma tutte le stringhe compatte è necessario compilarlo utilizzando l'opzione-XX:-CompactStrings
.
5. Utilizzando le compact string le performance del programma saranno sempre incrementate del 50%.
Soluzione
Tutte le affermazioni sono false. In particolare la numero 2 e la numero 3 sono scorrette perché un carattere di una stringa è immagazzinato in 16 bit (non una stringa), mentre se la stringa è compatta un suo carattere è immagazzinato utilizzando 8 bit. La numero 4 è scorretta perché l'opzione
-XX:-CompactStrings
, va utilizzata quando si esegue il programma non quando lo si compila. -
Esercizio F.d)
Qual è l'output del seguente file?
public class EsercizioFD { public static void main(String args[]) { String stringa ="*** Java ***"; stringa.toUpperCase(); stringa.trim(); stringa.substring(3, 8); stringa.trim(); stringa.concat(String.format("Stringa = %n", stringa.length())); stringa +="!"; System.out.println(stringa.length); } }
1. 12
2. 13
3. 11
4. 10
5. 23
6. 22
7. 24
8. Nessun output, sarà lanciata un'eccezione al runtime.
9. Nessun output, il file non è compilabile.
Soluzione
In teoria il risultato giusto sarebbe
13
, ma la risposta giusta in realtà è l'ultima.
Infatti nell'ultima istruzione mancano le parentesi tonde per invocare il metodolength
, il che provoca il seguente errore in compilazione:
EsercizioFD.java:10: error: cannot find symbol System.out.println(stringa.length); ^ symbol: variable length location: variable stringa of type String 1 error
Il compilatore ci avverte che non trova la variabilelength
(dato che mancano le parentesi tonde che caratterizzano la sintassi dei metodi). Notare che tutte le istruzioni (tranne la penultima), non riassegnano il risultato del metodo invocato astringa
, che quindi punta sempre allo stesso oggetto immutabile dichiarato inizialmente. Solo nella penultima istruzione viene riassegnata alla variabilestringa
un nuovo oggetto (che concatena un punto esclamativo alla fine della stringa) con l'operatore+=
. Ecco perché se non ci fosse l'errore all'ultimo statement, sarebbe stato stampato il valore13
. -
Esercizio F.e)
Qual è l'output del seguente file?
public class EsercizioFE { public static void main(String args[]) { String stringa ="Java"; stringa = stringa.concat(" "); stringa += 9; String risultato = ""; if (stringa.intern() == "Java 9") { risultato += "intern()"; } if (stringa == "Java 9") { risultato += "=="; } if (stringa.equals("Java 9")) { risultato += "equals()"; } System.out.println(risultato); } }
1.Java 9
2.intern()
3.intern()==
4.intern()equals()
5.null
6.intern()==equals()
7. Una stringa vuota.
8. Nessun output, sarà lanciata un'eccezione al runtime.
9. Nessun output, il file non è compilabile.
Soluzione
L'output della classe
EsercizioFE
è il seguente:
intern()==equals()
quindi la risposta giusta è la numero 6. Infatti la chiamata al metodointern
, prova a recuperare l'oggettoString
su cui è chiamato dal pool di stringhe, utilizzando il confronto che fornisce il metodoequals
. Nel caso nel pool non esista la stringa desiderata, questa viene aggiunta, e viene restituito un reference ad essa. Quindi la stringa viene aggiunta alla pool, e le successive condizioni degliif
, vengono verificate. -
Esercizio F.f)
Qual è l'output del seguente file?
public class EsercizioFF { public static void main(String args[]) { String stringa1 = "123789"; String stringa2 = stringa1.concat(System.lineSeparator()); char [] array1 = stringa2.toCharArray(); char [] array2 = {'4', '5', '6'}; System.arraycopy(array2, 0, array1, 3, 3); System.out.println(array1); System.exit(0); } }
Soluzione
L'output della classe
EsercizioFF
è il seguente:
123456
Si noti che l'output comprende l'andare da capo finale. Infatti dopo aver dato come valore123789
alla variabilestringa1
, otteniamostringa2
concatenando astringa1
un separatore di linea (che fa andare a capo) grazie al metodo staticolineSeparator
della classeSystem
. Chiamando il metodotoCharArray
sustringa2
, immagazziniamo inarray1
l'array di caratteri che costituivano la stringastringa2
. Quindi tale array ha dimensioni 8 se lanciato su Windows (doveSystem.lineSeparator
contiene due caratteri\r
e\n
), mentre ha dimensione 7 su altre piattaforme come Linux (doveSystem.lineSeparator
contiene solo il carattere di escape\n
). Poi all'arrayarray2
vengono assegnati tre elementi sempre di tipo carattere (4
,5
e6
), che vengono copiati tutti tramite il metodoSystem.arraycopy
nell'array1 a partire dall'indice 2. Infine si stampa l'arrayarray2
, e poi si esce dal programma tramite il metodoSystem.exit
(superfluo in questo caso perché il programma sarebbe terminato lo stesso subito dopo). -
Esercizio F.g)
Scrivere una classe che rappresenta un testo di tipo RTF (Rich Text Format), ovvero un testo a cui è possibile modificare per esempio il carattere, il colore di sfondo, l'interlinea, la sottolineatura e così via (scegliete voi cosa deve definire). Rendere tale classe clonabile e creare un programma di test che verifica l'effettivo funzionamento.
Soluzione
La soluzione potrebbe essere implementata nella seguente maniera. Creiamo la seguente enumerazione che definisce qualche tipologia di font semplicemente.
public enum Font { ARIAL, TIMES_NEW_ROMAN, COURIER, MONOSPACED; }
Poi creiamo la classeTestoRTF
richiesta in questa maniera:
public class TestoRTF implements Cloneable { String testo; Font carattere; boolean sottolineato; public TestoRTF (String testo, Font carattere, boolean sottolineato){ this.testo = testo; this.carattere = carattere; this.sottolineato = sottolineato; } public Object clone() throws CloneNotSupportedException { return super.clone(); } public String toString(){ return "Testo = " + testo + ", carattere = " + carattere + ", sottolineato = " + sottolineato; } }
Infine possiamo scrivere la classe di test in questa maniera:
public class EsercizioFG { public static void main(String args[]) throws CloneNotSupportedException { TestoRTF testoRTF1 = new TestoRTF("Java", Font.ARIAL, false); System.out.println(testoRTF1); System.out.println(testoRTF1.clone()); } }
Che produrrà il seguente output.
Testo = Java, carattere = ARIAL, sottolineato = false Testo = Java, carattere = ARIAL, sottolineato = false
-
Esercizio F.h)
Partendo dall'esercizio 5.w, Creare una classe con metodo
main
che legge username e password, come properties di sistema. Testare la classe utilizzando le giuste opzioni da riga di comando (specificarle).
Soluzione
Una delle possibili soluzioni è la ridefinizione della classe
Autenticazione
:
package com.claudiodesio.autenticazione; import java.util.Scanner; public class Autenticazione { public void login() { String username = System.getProperty("username"); System.out.println(username); String password = System.getProperty("password"); System.out.println(password); Utente utente = trovaUtente(username); if (utente != null) { if (verificaPassword(utente, password)) { Stampa.auguraBenvenuto(utente.getNome()); } else { Stampa.autenticazioneFallita(); } } else { Stampa.usernameInesistente(); } } private Utente trovaUtente(String username) { Utente[] utenti = ProfiliUtenti.getInstance().getUtenti(); if (username != null) { for (Utente utente : utenti) { if (username.equals(utente.getUsername())) { return utente; } } } return null; } private boolean verificaPassword(Utente utente, String password) { boolean trovato = false; if (password != null) { if (password.equals(utente.getPassword())) { trovato = true; } } return trovato; } public static void main(String args[]) { Autenticazione autenticazione = new Autenticazione(); autenticazione.login(); } }
che lanciata con questi parametri:
java -Dusername=dansap -Dpassword=musica com.claudiodesio.autenticazione.Autenticazione
darà luogo al seguente output:
dansap musica Benvenuto Daniele
-
Esercizio F.i)
Quali delle seguenti affermazioni sono corrette riguardo la garbage collection:
1. La JVM implementa solo due algoritmi per deallocare la memoria: G1GC e ParallelGC.
2. Con ParallelGC gli interventi sulla memoria sono più numerosi e intensi, e questo rende l'algoritmo meno efficiente rispetto a G1GC.
3. Era possibile utilizzare G1GC già dalla versione 7 di Java.
4. La "finalizzazione" consiste nell'eliminare gli oggetti non più utilizzati dall'applicazione.
5. La "finalizzazione" può essere invocata mediante la chiamata del metodorunFinalization
della classeObject
.
6. Per utilizzare ParallelGC, è necessario lanciare l'applicazione con l'applicazione specificando l'opzione-XX:+UseParallelGC
.
Soluzione
Sono corrette le affermazioni numero 3 e 6. La numero 1 è falsa perché in realtà è possibile utilizzare anche altri algoritmi come SerialGC. Nell'affermazione numero 2 la prima parte della frase non è corretta: gli interventi della ParallelGC sono meno frequenti (ma più intensi). La "finalizzazione" consiste nel testare se esistono oggetti non più "raggiungibili" da qualche reference e quindi non utilizzabili, quindi anche l'affermazione 4 è errata. Il metodo
runFinalization
richiede la finalizzazione degli oggetti, ma risiede nella classeRuntime
nonObject
, ecco perché anche l'affermazione 5 è errata. -
Esercizio F.j)
Data la seguente classe:
public class EsercizioFJ { public static void main(String args[]) { int raggio = 7; /*INSERISCI CODICE QUI*/ System.out.println("L'area della circonferenza di raggio 7 è " + area); } }
definire la riga che deve sostituire il commento
/*INSERISCI CODICE QUI*/
per ottenere il corretto calcolo dell'area.
Ricordiamo che l'area di una circonferenza viene calcolata come: pi greco per raggio al quadrato, ovvero se chiamiamo il raggior
, eA
l'area della circonferenza, e indichiamo il numero pi greco conπ
, avremo:
A = πr²
Consultare la documentazione ufficiale della classeMath
prima di scrivere il codice.
Soluzione
Basterà utilizzare la variabile
PI
(che rappresenta ilπ
della classeMath
), e il metodopow
(che rappresenta la funzione potenza nel seguente modo:
public class EsercizioFJ { public static void main(String args[]) { int raggio = 7; double area = Math.PI * Math.pow(raggio, 2); System.out.println("L'area delle circonferenza di raggio 7 è " + area); } }
E questo è il suo output:
L'area delle circonferenza di raggio 7 è 153.93804002589985
-
Esercizio F.k)
Qual è l'output del seguente file?
public class EsercizioFK { public static void main(String args[]) { double e = Math.E; Math.floor(e); boolean b = check(e, 2.0); System.out.println(b); } public static Boolean check(Double a, Double b){ Boolean equals = null; if (a.equals(b)){ equals = true; } return equals; } }
1.true
2.false
3.null
4.2.0
5.2.718281828459045
6. Nessun output, sarà lanciata un'eccezione al runtime.
7. Nessun output, il file non è compilabile.
Soluzione
La risposta corretta è la 6, infatti l'output del è il seguente:
Exception in thread "main" java.lang.NullPointerException at EserczioFK.main(EserczioFU.java:5)
questo perché il risultato della chiamata del metodofloor
, non viene riassegnato a nessuna variabile, e quindi la variabilee
rimane con il valore iniziale (2.718281828459045
). Quindi il metodocheck
ritornanull
, perché2.0
è diverso da2.718281828459045
. Manull
non si può assegnare ad un tipo primitivoboolean
. -
Esercizio F.l)
Qual è l'output della seguente classe?
public class EsercizioFL { public static void main(String args[]) { EsercizioFL e = new EsercizioFL(); e.metodo(128); } public void metodo(Integer numero) { System.out.println("Integer " + numero); } public void metodo(long numero) { System.out.println("long " + numero); } public void metodo(byte numero) { System.out.println("byte " + numero); } public void metodo(Byte numero) { System.out.println("Byte " + numero); } public void metodo(short numero) { System.out.println("short " + numero); } public void metodo(Double numero) { System.out.println("Double " + numero); } public void metodo(double numero) { System.out.println("double " + numero); } }
Soluzione
L'output della classe
EsercizioFL
, è il seguente:
long 128
Infatti, come abbiamo asserito nel capitolo 3, il valore128
viene considerato di default unint
. Quindi, nonostante l'autoboxing sia sempre valido, viene invocato il metodo che ha come parametro un tipo primitivo, per ragioni di retro-compatibilità del codice, come spiegato nel paragrafo F.5.1.7.