Java
the missing parts
Francesco Komauli
Studente di Ingegneria dell'Informazione
Università di Trieste
Programmatore nel tempo libero
Scherma medievale, lancio della scure
Scherma medievale, lancio della scure
Accademia Jaufré Rudel, Gradisca d'Isonzo
Ereditarietà
&
Polimorfismo
ereditarietà
-
Classi che definiscono oggetti
-
Una classe figlia eredita le caratteristiche della classe parent
- Classe root della gerarchia (Object in Java)
ereditarietà
-
L'ereditarietà viene utilizzata per organizzare le astrazioni come gerarchie di classi, in cui determinati concetti ne estendono altri più generici.
- Ampio riutilizzo del codice della classe parent.
ereditarietà
-
Supponiamo di avere come classe root la classe Animal, e di dover costruire una gerarchia degli animali.
-
Prendiamo come esempio le classi animali (il termine classi ora è inteso nel contesto della classificazione scientifica).
class Animal {
void drink(Water water) {...}
void eat(Food food) {...}
}
ereditarietà
class Fish extends Animal {
void swim() {...}
Egg layEgg() {...}
}
class Reptile extends Animal {
void slither() {...}
Egg layEgg() {...}
}
class Bird extends Animal {
void fly() {...}
Egg layEgg() {...}
}
class Mammal extends Animal {
void walk() {...}
Cub breed() {...}
}
ereditarietà
-
Abbiamo descritto alcune azioni che gli animali di determinate classi possono eseguire.
- Tuttavia qualcosa non va per il verso giusto.
ereditarietà
Duplicazione del codice
class Fish extends Animal {
void swim() {...}
Egg layEgg() {...}
}
class Reptile extends Animal {
void walk() {...}
Egg layEgg() {...}
}
class Bird extends Animal {
void fly() {...}
Egg layEgg() {...}
}
ereditarietà
Duplicazione del codice
-
L'ereditarietà ci viene incontro: possiamo muovere il codice duplicato in una superclasse.
- Definiamo la classe Oviparous da cui tutti tranne i mammiferi erediteranno.
class Oviparous extends Animal {
Egg layEgg() {...}
}
ereditarietà
class Fish extends Oviparous {
void swim() {...}
}
class Reptile extends Oviparous {
void walk() {...}
}
class Bird extends Oviparous {
void fly() {...}
}
class Mammal extends Animal {
void walk() {...}
Cub breed() {...}
}
ereditarietà
Duplicazione del codice
SOLVED!

ereditarietà
Ora andiamo ad implementare una classe più specifica, nella categoria delle specie animali.
Ad esempio: Platypus (ornitorinco)
ereditarietà

ereditarietà
L'ornitorinco è un mammifero ma depone uova...
Abbiamo sbagliato astrazione? Evidentemente sì, ma perché?
Abbiamo sbagliato astrazione? Evidentemente sì, ma perché?
- La gerarchia che la classificazione scientifica propone si basa su ciò che gli animali sono (codice genetico ed evoluzione) e non su ciò che fanno.
- Le classi che andiamo a progettare definiscono cosa un'entità sia attraverso la definizione dello stato (variabili membro), ma espongono un comportamento (metodi).
ereditarietà
L'ereditarietà si basa su cosa un oggetto sia per poter propagare alle classi figlie le sue proprietà, ovvero:
L'ereditarietà definisce una relazione di tipo
IS-A
Il riutilizzo del codice è quindi solo un effetto collaterale benigno dell'ereditarietà. Al prezzo di dover costruire una gerarchia anche dove non sia necessaria.
Polimorfismo
- Definizione di un contratto che una data classe deve soddisfare, incentrato quindi sulle operazioni che un oggetto di tale classe espone.
- Java fornisce questo meccanismo attraverso il concetto di interface. Un'interfaccia è un insieme di firme di metodi che le classi implementanti devono esporre pubblicamente.
Polimorfismo
Trasformiamo la classe Oviparous
in un'interfaccia.
interface Oviparous {
Egg layEgg();
}
Ora anche la classe Platypus può implementare Oviparous pur continuando ad ereditare da Mammal .
class Platypus extends Mammal implements Oviparous {
Egg layEgg() {...}
}
Polimorfismo
Attraverso l'uso del polimorfismo è possibile:
- evitare di esporre variabili membro in modo che le sottoclassi possano farvi riferimento, violando l'incapsulamento;
- aderire a più di un contratto, mentre è possibile ereditare da una e una sola classe (in Java);
- fornire diverse implementazioni, le classi fanno la stessa cosa ma sono diverse.
Polimorfismo
Duplicazione del codice

Polimorfismo
class Fish extends Animal implements Oviparous {
void swim() {...}
Egg layEgg() {...}
}
class Reptile extends Animal implements Oviparous {
void walk() {...}
Egg layEgg() {...}
}
class Bird extends Animal implements Oviparous {
void fly() {...}
Egg layEgg() {...}
}
class Platypus extends Mammal implements Oviparous {
Egg layEgg() {...}
}
Polimorfismo
Duplicazione del codice
Ogni classe deve implementare il metodo
layEgg()
, e può capitare che possa contenere lo stesso codice. Ad esempio quello che avremmo scritto nella classe
Oviparous
, sostituita poi dall'omonima interfaccia
.
Polimorfismo
Composition over Inheritance
Possiamo predisporre le classi che implementano Oviparous per ricevere in costruzione un oggetto che ha il compito di definire una strategia di deposizione delle uova.
Il metodo layEgg() non dovrà fare altro che utilizzare tale oggetto, senza definire altro codice che riguardi le uova.
Polimorfismo
Duplicazione del codice
SOLVED!



Polimorfismo
Duplicazione del codice
SOLVED! (Maybe)



Classi anonime
Classi anonime
Uno degli strumenti che Java offre è l'implementazione "al volo" di un'interfaccia. Non è quindi necessario far sempre risiedere l'implementazione in una classe separata.
Classi anonime
Implementazione separata
public class CompareByStringLength implements Comparator<String> {@Overridepublic int compare(String a, String b) {return a.length() - b.length();}}
List<String> listOfStrings = Arrays.asList("aaa", "bb", "c", "aa");Collections.sort(listOfStrings, new CompareByStringLength());
La lista dopo la chiamata a sort conterrà gli elementi nell'ordine:
"c" "bb" "aa" "aaa"
Classi anonime
Implementazione ANONIMA
List<String> listOfStrings = Arrays.asList("aaa", "bb", "c", "aa");Collections.sort(listOfStrings, new Comparator<String>() {@Overridepublic int compare(String a, String b) {return a.length() - b.length();});
L'implementazione viene creata sul momento e fornita al metodo come argomento. Non sarà disponibile in altre parti del programma perché non si è tenuto alcun riferimento.
Classi anonime
COMING SOON
λ
List<String> listOfStrings = Arrays.asList("aaa", "bb", "c", "aa");Collections.sort(listOfStrings, (a, b) -> a.length() - b.length());
We are Anonymous

Implement Us.
Java Collections
Java Collections
-
List
: sequenza ad accesso casuale e di lunghezza variabile.
-
Set
: raccoglie elementi senza mantenere duplicati
-
Map
: dizionario che associa ad ogni valore una chiave univoca
-
Queue
: struttura dati FIFO
- Deque : double ended queue (pronunciato "deck")
Java Collections
List
- Consente di inserire, sostituire, recuperare ed eliminare un elemento ad un dato indice.
- Mantiene l'ordine di inserimento degli elementi.
- Consente di inserire più volte lo stesso elemento, che occuperà diverse posizioni.
- Comodo sostituto degli array.
Java Collections
List
-
ArrayList
Lista che utilizza un array per memorizzare gli elementi.
Accesso casuale in tempo costante, ma rimozione ed aggiunta non in coda in tempo lineare.
-
LinkedList
Formata da una doppia catena. Accesso casuale in tempo lineare.
Java Collections
Set
- Mantiene uno solo tra gli elementi considerati uguali (per i quali equals
restituisce
true
).
-
Possibilità di definire un ordinamento.
Java Collections
Set
-
HashSet
Utilizza una tabella hash per memorizzare gli elementi. Accesso in tempo costante.
-
TreeSet
Implementato con un albero binario di ricerca. Accesso in tempo logaritmico rispetto al numero di elementi. Vi è possibile definire un ordine degli elementi.
Java Collections
Map
- Costituita da coppie chiave-valore.
- Le chiavi memorizzate in una mappa sono univoche, mentre uno stesso elemento può essere inserito più volte con diverse chiavi.
- Gli elementi vengono recuperati specificando la chiave.
Java Collections
Map
-
HashMap
Utilizza una tabella hash per memorizzare gli elementi. Accesso in tempo costante.
-
TreeMap
Implementato con un albero binario di ricerca. Accesso in tempo logaritmico rispetto al numero di elementi. Vi è possibile definire un ordine degli elementi.
Java Collections
Map
Forse avrete notato che le descrizioni delle implementazioni di Set
e
Map
sono identiche.
Infatti le implementazioni di
Set
utilizzato la relativa implementazione di
Map
come mappa di sole chiavi.
Java Collections
Queue e Deque
-
Queue
viene utilizzata per inserire elementi in coda ed estrarli dalla testa. Questo tipo di struttura è detta FIFO (first in, first out)
-
Deque
permette di fare anche il contrario, ovvero inserire elementi in testa e recuperarli dalla coda.
-
Deque può essere utilizzata anche come stack, ovvero inserendo gli elementi in testa e recuperandoli sempre dalla testa.
Java Collections
Queue e Deque
-
LinkedList può essere utilizzata sia come queue che deque.
-
ArrayDeque risulta essere più performante di LinkedList quando usata come coda.
- PriorityQueue non restituisce gli elementi nell'ordine di inserimento, ma con un ordinamento specificato. Infatti è implementato con uno heap.
Java Collections
Pattern Iterator
- Spesso non interessa quale collection venga utilizzata, ad esempio non ci troviamo nella parte dell'applicazione che deve preoccuparsi di instanziare le strutture dati.
- Le collection di Java implementano il pattern iterator, che consente di accedere in sequenza agli elementi che contengono senza dover conoscere la loro struttura né i loro particolari metodi.
Java Collections
Iterable e Iterator
-
Iterable
Interfaccia attraverso la quale si ottiene un iteratore sulla collection. Tutte le collection di Java implementano Iterable, tranne Map (ma è posibile iterare sull'insieme delle coppie chiave-valore).
-
Iterator
Fornisce un metodo per scoprire se ci sia ancora un altro elemento e uno per ottenerlo.
Java Collections
Costrutto foreach
Un Iterable
può essere utilizzato nel seguente modo.
Iterable<E> iterable = ...
Iterator<E> iterator = iterable.iterator();
while (iterator.hasNext()) {
E element = iterator.next();
// do something with the element
}
Java Collections
Costrutto foreach
Un modo più conciso per eseguire questo tipo di operazione è dato dal costrutto foreach, che consente di dichiarare unicamente che operazione intraprendere con gli elementi.
Iterable<E> iterable = ...
for (E element : iterable) {
// do something with the element
}
Exceptions
Exceptions
Codici d'errore
final int ERROR_OK = 0;
final int ERROR_NULL = 3;
...
int performOperation() {
Result result = operation();
if (result == null) {
return ERROR_NULL;
}
result.show();
return ERROR_OK;
}
int errorCode = performOperation();
if (errorCode == ERROR_OK) {
System.out.println("operation performed");
} else if (errorCode == ERROR_NULL) {
System.out.println("error: cannot perform operation");
} ...
Exceptions
Codici d'errore
- Limitata scalabilità: aggiungere codici d'errore porterebbe alla necessità di modificare alcune parti che li gestiscono.
- Gli errori vanno gestiti subito dopo l'operazione interessata, altrimenti toccherebbe restituire intere liste di codici di errore per poterle gestire altrove.
- La logica di gestione degli errori si confonde con la logica di funzionamento del metodo.
Exceptions
Eccezioni
void performOperation() {
Result result = operation();
if (result == null) {
throw new IllegalStateException("cannot perform operation");
}
result.show();
}
try {
performOperation();
System.out.println("operation performed");
} catch (IllegalStateException exception) {
System.out.println(exception.getMessage());
}
Exceptions
Eccezioni
- Lanciare un'eccezione garantisce la sospensione dell'esecuzione in corso (lo stack di chiamate viene semplicemente risalito). Non serve quindi biforcare nel codice l'esecuzione in base agli errori.
- Un'eccezione può essere catturata o propagata.
- I metodi chiamanti non devono necessariamente sapere che un metodo che invocano potrebbe lanciare un'eccezione. Ottimo per la scalabilità del codice.
Exceptions
Quando lanciare un'eccezione
- Il metodo si aspetta di ricevere un sottoinsieme del dominio degli argomenti.
- Ad esempio per ottenere un elemento da una lista, l'indice deve essere non negativo e minore della sua lunghezza. In caso contrario il metodo delega al chiamante la gestione della situazione attraverso un'eccezione.
- In Java: IllegalArgumentException
Exceptions
Quando lanciare un'eccezione
- L'oggetto ha raggiunto uno stato per il quale un'operazione non è più effettuabile.
- Ad esempio un iteratore consumato non può restituire un prossimo elemento. Se si richiede un nuovo elemento, l'iteratore lancia un'eccezione.
- In Java: IllegalStateException
Exceptions
Checked & Unchecked
In Java viene fatta distinzione tra le eccezioni di tipo checked e di tipo unchecked.
- Un'eccezione unchecked può essere lanciata senza dover dichiarare ciò nella firma del metodo.
- Un'eccezione checked deve essere catturata nel corpo del metodo, oppure nella sua firma deve essere dichiarata esplicitamente la propagazione al chiamante.
Exceptions
Checked & Unchecked
La distinzione viene fatta essenzialmente per gestire due tipi diversi di errori.
- Le eccezioni unchecked vengono utilizzate per segnalare un utilizzo scorretto di un oggetto.
- Le eccezioni checked sono usate per segnalare situazioni anomale che non dipendono dallo stato del programma (errori di I/O, networking, ...). In questo modo chi utilizza tali metodi è conscio dei tipi di errori che si possono occasionalmente verificare.
Generics
Generics
In Java esiste la possibilità di parametrizzare un tipo.
Ad esempio una lista può essere parametrizzata in base al tipo di oggetti che deve contere.
- List<Integer>
- List<String>
- List<Runnable>
- ...
Generics
L'interfaccia List
viene dichiarata in questo modo.
public interface List<E> extends Collection<E> {
...
public E get(int index);
...
}
Generics
Dichiarando di utilizzare una lista di stringhe è come se esistesse la seguente interfaccia.
List<String> stringsList = new LinkedList<>();
public interface List extends Collection {
...
public String get(int index);
...
}
Generics
Non viene generato codice aggiuntivo.
Questo meccanismo serve al compilatore per verificare che vengano usati solo i tipi consentiti.
List<String> stringsList = new LinkedList<>();
stringsList.add("I'm a string! Yay!");
stringsList.add(new Integer(5)); // ERRORE DI COMPILAZIONE@Annotations
@Annotations
-
Un'annotazione è un metadato utilizzato per identificare una parte del programma.
- Vengono utilizzate per facilitare operazioni a strumenti che operano sul codice (sorgente o bytecode), come compilatori, framework, ...
@Annotations
Esempio
@Test
public void theSumOfTwoPositiveNumbersIsPositive() {
int sum = 2 + 9;
Assert.assertTrue(sum > 0);
}
Il framework di unit testing (JUnit) esegue automaticamente tutti i metodi annotati con @Test.
Meno configurazione e più manutenibilità.
Grazie
Follow:
@unitsdev
@0x1abe5
java missing parts
By labes
java missing parts
- 3,103