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.
C18-G01 – Quattro domande sul catalogo
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.