mattone
dopo mattoneLA COLLANA
IT/EN
← Guida Java

Java 25 · 33/39

Attività C13–C18, prima stesura

Java 25 · Guida completa · Bozza in revisione

Questa guida conserva lo stato di revisione del libro. La verifica editoriale e le prove di comprensione con lettori indipendenti sono ancora da completare.

Cerca in tutta la guida →

Queste prove accompagnano le bozze dei capitoli. Si eseguono con JDK 25 senza preview, su copie dei sorgenti: le varianti volutamente errate non devono restare nei programmi positivi. Ogni attività indica una risposta attesa, così il lettore può confrontare il ragionamento con il comportamento osservato. La prova con lettori e i gate EV0–EV5 restano aperti.

C13-A01 – Due implementazioni, un riferimento

Consegna. In DemoInterfacce.java, assegna una Temperatura a un riferimento Misurabile e prova a chiamare prima valore(), poi descrizione(). Spiega quale chiamata compila e quale no, pur riferendosi allo stesso oggetto.

Criterio e soluzione. valore() compila perché è nel contratto Misurabile; descrizione() non è visibile dal tipo dichiarato del riferimento. L'oggetto resta una Temperatura, ma il compilatore usa il tipo del riferimento per stabilire le operazioni disponibili. Una nuova variabile Descrivibile può riferirsi allo stesso oggetto e chiamare descrizione().

C13-P01 – Conflitto dei default

Consegna. Prima di compilare DemoConflittoDefault.java, prevedi la riga stampata. In una copia togli l'override di nome() da Scelta e compila.

Criterio e soluzione. La versione corretta stampa primo+secondo. Senza override la classe riceverebbe due metodi default incompatibili e il compilatore rifiuta la dichiarazione; l'ordine di implements non risolve il conflitto.

C13-D01 – Un esito nuovo

Consegna. Aggiungi Sospeso(String motivo) alle implementazioni consentite dall'interfaccia sealed di DemoEsito.java. Compila prima di cambiare lo switch, poi aggiungi il ramo mancante.

Criterio e soluzione. Dopo l'aggiunta del tipo, lo switch non copre più tutti gli esiti ammessi e non compila. Un ramo case Sospeso sospeso -> ... ristabilisce l'esaustività. Il lettore sa spiegare perché un default nasconderebbe la necessità di rivedere il significato del nuovo esito.

C14-A01 – Fino a quando esiste un'annotazione?

Consegna. Esegui DemoAnnotazioni.java con Etichetta.java. In una copia cambia la retention da RUNTIME a CLASS, ricompila e riesegui.

Criterio e soluzione. La versione iniziale stampa catalogo e didattica; la seconda non stampa etichette perché la normale reflection non le vede a runtime. La compilazione resta valida: la retention controlla la disponibilità successiva del metadato, non il significato delle due stringhe.

C14-G01 – Riconoscere un processor

Consegna. Compila i tre sorgenti di C14, poi invoca esplicitamente EtichettaProcessor con -processor EtichettaProcessor -proc:only. Indica se la nota del compilatore compare prima o dopo l'avvio di main.

Criterio e soluzione. La nota etichetta presente su DemoAnnotazioni compare durante javac; main non viene avviato da -proc:only. Un processor lavora sul modello del programma in compilazione, mentre getAnnotationsByType è una lettura a runtime.

C14-P01 – Un controllo dichiarato

Consegna. Progetta @Responsabile per un metodo: scegli @Target, @Retention e un elemento String value(). Descrivi quale componente leggerà l'annotazione, senza sostenere che Java invierà automaticamente una notifica al responsabile.

Criterio e soluzione. ElementType.METHOD limita la collocazione. RUNTIME è giustificata se un'applicazione la leggerà con reflection; SOURCE o CLASS possono bastare per un controllo di compilazione. Il valore è un metadato e richiede codice separato per produrre effetti.

C14-D01 – Ereditarietà e annotazioni ripetibili

Consegna. Prevedi le quattro righe di DemoPolitiche.java, poi le due etichette di DemoPromemoria.java. Spiega perché getDeclaredAnnotationsByType e getAnnotationsByType rispondono a domande diverse sulla sottoclasse e perché due promemoria non attivano da soli uno scheduler.

Criterio e soluzione. L'annotazione della base può essere visibile sulla derivata grazie a @Inherited, ma non diventa una dichiarazione scritta sulla derivata. Le due annotazioni ripetute restano due valori leggibili in ordine dichiarato; per usarli come orari di esecuzione occorre un componente applicativo separato.

C15-A01 – Perché il cast sparisce?

Consegna. In DemoGenerics.java, individua dove il compilatore conosce il tipo restituito da titoli.estrai(). Prova poi a passare 3 a titoli.inserisci in una copia del file.

Criterio e soluzione. Pila<String> fissa T a String nelle firme dei metodi usate dal chiamante. L'inserimento dell'intero non compila; non occorre attendere l'estrazione per scoprire l'incompatibilità. La spiegazione non attribuisce il controllo a un cast scritto a mano.

C15-P01 – Leggere e scrivere con wildcard

Consegna. Modifica copiaNumeri per leggere da List<? extends Integer> in una variabile Integer e ricevere List<? super Integer> come destinazione. Prova List<Number> e List<Object>; poi tenta di leggere direttamente un Integer dalla destinazione.

Criterio e soluzione. Entrambe le liste possono ricevere un Integer, ma il tipo garantito in lettura dal parametro con super è soltanto Object. La direzione della wildcard segue l'operazione che il metodo deve compiere.

C15-D01 – Un'assegnazione tentatrice

Consegna. Aggiungi List<Number> numeri = List.of(1, 2); e, su una riga separata, prova ad assegnare una variabile List<Integer> a List<Number>. Distingui i due casi.

Criterio e soluzione. Il primo può compilare perché il compilatore costruisce una lista nel contesto del tipo richiesto. Il secondo non compila: una List<Integer> già tipizzata non diventa una List<Number>, altrimenti si potrebbe inserirvi un Double.

C15-G01 – Il ponte dopo l'erasure

Consegna. Compila DemoPonte.java e ispeziona DepositoTitoli con javap -p. Individua il metodo con parametro String e quello con parametro Object. Spiega quale problema di dispatch risolve il secondo, senza sostenere che il codice sorgente abbia dichiarato due overload volontari.

Criterio e soluzione. Dopo l'erasure la firma ereditata usa Object; il compilatore aggiunge un metodo ponte che adatta la chiamata al metodo specializzato con String. La relazione generica resta verificata dal compilatore al punto d'uso.

C16-A01 – Leggere la composizione

Consegna. Prima di eseguire DemoFunzioni.java, segui " Java " attraverso strip, toUpperCase, il predicato e la stampa. Ripeti per " Go ".

Criterio e soluzione. Il primo testo diventa JAVA e produce JAVA: 4; il secondo diventa GO ma non supera la lunghezza minima di 3. La risposta distingue trasformazione, decisione ed effetto di stampa.

C16-P01 – Cattura di un valore locale

Consegna. Sposta il controllo della lunghezza in Predicate<String> abbastanzaLungo = testo -> testo.length() >= lunghezzaMinima;. Poi prova a incrementare lunghezzaMinima dopo la dichiarazione.

Criterio e soluzione. La lambda funziona finché la variabile locale è effectively final. Dopo l'incremento, il compilatore rifiuta la cattura. Togliere final dalla dichiarazione non cambia questa regola.

C16-D01 – Ricorsione con arresto

Consegna. Disegna le chiamate di fattoriale(3) e prevedi il risultato. Prova poi fattoriale(-1) in una copia e spiega perché il controllo iniziale è necessario.

Criterio e soluzione. Le chiamate arrivano a fattoriale(0), che restituisce 1; risalendo otteniamo 1, 2 e 6. Il valore negativo lancia IllegalArgumentException, evitando una discesa senza un caso di arresto adatto.

C16-G01 – La stessa operazione in tre forme

Consegna. In DemoPercorsoLambda.java, segui i valori 10 e 3 nelle implementazioni anonima, lambda e method reference. Cambia aggiungi(2) in aggiungi(5) e prevedi l'ultima riga. Poi esegui DemoCattura.java e spiega perché le sue due righe sono diverse.

Criterio e soluzione. Le prime due implementazioni sottraggono e stampano 7, il riferimento a somma produce 13; aggiungi(5) produce 18. In DemoCattura il valore locale 3 resta stabile, mentre il campo totale cambia fra le chiamate, producendo 3 e 6. Il lettore distingue valore catturato e stato mutabile dell'oggetto.

C17-A01 – Duplicati e ordine

Consegna. Prevedi le prime tre righe di DemoCollezioni.java. Indica quale contratto conserva il secondo Java e quale lo elimina. Poi trasferisci la scelta a un nuovo caso: la biblioteca deve memorizzare due prestiti successivi dello stesso libro e, separatamente, i codici dei libri già mostrati in vetrina. Scegli i due tipi di collezione e spiega perché non puoi usarne uno solo senza perdere una proprietà richiesta.

Criterio e soluzione. Le righe sono [Java, Reti, Java], [Java, Reti], [Reti, Java]. List conserva duplicati e posizioni; LinkedHashSet conserva unicità e ordine d'incontro; reversed() offre la vista opposta. I due prestiti sono eventi distinti e restano in una List<Prestito>, anche se riguardano lo stesso libro. I codici già esposti formano invece un Set<String>; LinkedHashSet serve se conta la prima esposizione. La risposta deve associare ogni scelta al requisito, non solo nominare una classe.

C17-P01 – La pila della libreria

Consegna. In una copia del programma, aggiungi un terzo valore alla Deque e prevedi l'ordine di tre chiamate a pop(). Spiega in che cosa questo uso si collega alla pila costruita nei capitoli precedenti. Ora cambia il problema: le richieste devono uscire nell'ordine di arrivo. Quale contratto useresti e quali operazioni sostituiresti?

Criterio e soluzione. I valori escono nell'ordine inverso agli inserimenti con push. L'interfaccia è generica e l'implementazione ArrayDeque sostituisce per uso ordinario la pila didattica, senza cambiare il principio LIFO. Per la nuova richiesta FIFO si usa il contratto Queue o Deque, inserendo in coda con offer e prelevando dalla testa con poll o remove secondo la politica scelta per la coda vuota. La struttura concreta può restare ArrayDeque; cambia il significato delle operazioni.

C17-D01 – Il comparatore che perde un titolo

Consegna. Togli thenComparing(Comparator.naturalOrder()) dal TreeSet del programma ed esegui. Spiega perché uno dei titoli di quattro lettere manca. Proponi poi un criterio che ordini prima per lunghezza e, a parità, conservi titoli diversi; indica anche un caso in cui scegliere direttamente un HashSet e ordinare soltanto la presentazione sarebbe più semplice.

Criterio e soluzione. Java e Reti hanno la stessa lunghezza; un comparatore che guarda soltanto la lunghezza restituisce zero e il set li tratta come equivalenti. Un secondo confronto alfabetico distingue i due titoli. Se il requisito è solo evitare duplicati e l'ordine serve in una stampa occasionale, si può conservare un HashSet e ordinare una copia al momento della presentazione. Il criterio di ordinamento di TreeSet decide anche l'unicità in quella implementazione.

C18-A01 – Sorgente e risultato

Consegna. Prevedi le prime due righe di DemoStream.java e indica quale operazione crea la nuova lista.

Criterio e soluzione. La prima è [1, 2, 4, 5, 6]; la seconda è [1, 2, 3, 4, 5, 6, 3]. filter descrive la selezione; toList consuma lo stream e materializza un risultato senza modificare la sorgente.

C18-P01 – Finestre e aggregazioni

Consegna. Spiega perché windowFixed(3) produce tre gruppi e quale sia la somma stampata da teeing. Poi cambia la dimensione della finestra a 2 e prevedi i gruppi.

Criterio e soluzione. Sette elementi producono finestre [1, 2, 3], [4, 5, 6], [3]; la somma è 24. Con dimensione 2 otteniamo [1, 2], [3, 4], [5, 6], [3]. I due collector di teeing leggono la stessa sequenza logica per contare e sommare.

C18-D01 – Un stream già consumato

Consegna. Salva dati.stream() in una variabile e invoca due volte toList() sulla stessa variabile. Prova poi a chiamare dati.stream() due volte in due espressioni separate.

Criterio e soluzione. Lo stesso oggetto Stream non può essere consumato due volte; la seconda terminale fallisce. La List rimane disponibile e può produrre due stream distinti. La risposta distingue il ciclo di vita della pipeline da quello della collezione.

Consegna. Prima di eseguire DemoCatalogoStream.java, prevedi i titoli disponibili ordinati, il numero di libri del reparto Informatica, la somma delle pagine e il titolo disponibile più breve per pagine. Aggiungi poi un libro non disponibile con 50 pagine: quali risposte cambiano?

Criterio e soluzione. I risultati iniziali sono [Algebra, Algoritmi, Java], 3, 1150 e Algebra. Se il nuovo libro appartiene a Informatica, il conteggio del reparto sale a 4 e la somma delle pagine a 1200; la lista dei disponibili e il minimo disponibile restano invariati. Se scegli un altro reparto, devi adeguare la previsione del conteggio: il dato scelto fa parte del problema.

Prova gli esempi

Per eseguire i programmi serve JDK 25. Puoi scaricare i singoli file Java collegati nel capitolo oppure il progetto completo, che contiene istruzioni e uno script di avvio. Le spiegazioni confrontano anche l’output atteso: prevedilo prima di eseguire il programma.

Massimiliano Tarquini · CC BY-NC 4.0

Torna all’inizio ↑