Java: il linguaggio
Docente: Gabriele Lombardi
[email protected]
© 2010 - CEFRIEL
The present original document was produced by CEFRIEL and the Teacher for the benefit and internal use of this
course, and nobody else may claim any right or paternity on it. No right to use the document for any purpose other than
the Intended purpose and no right to distribute, disclose, release, furnish or disseminate it or a part of it in any way or
form to anyone without the prior express written consent of CEFRIEL and the Teacher.
© copyright Cefriel and the Teacher-Milan-Italy-23/06/2008. All rights reserved in accordance with rule of law and
international agreements.
© 2010 - CEFRIEL
Sommario
SLIDE
CONTENUTO
Sintassi base
Poco più del procedurale
Interfacce e classi
Strumenti per l’OOP
Ereditarietà e
implementazione
Strumenti di riutilizzo del codice.. e di astrazione
Enums e annotations
Da Tiger in avanti (jdk >= 1.5)
Gestione delle eccezioni
In sostituzione ai controlli di codici di errore
Generics
Per ADT type-safe.. con type-erasure
Altro
Zucchero sintattico
© 2010 - CEFRIEL
Intro: codice compilato VS interpretato

Codice compilato:
– veloce.. ma non portabile.

Codice interpretato:
– portabile.. ma lento.

virtual machine (JVM):
–
–
–
–

codice compilato per una macchina inventata (bytecode);
interpretato da una virtual machine;
portabile (dove una JVM sia presente);
veloce (meno del codice compilato nativo);
JIT (massimo della vita):
–
–
–
–
–
come precedente, ma …
… traduzione a run-time in codice nativo;
sparisce una vera e propria interpretazione;
viene eseguito codice nativo;
portabile (ove esista la JVM).. Ma veloce quanto (o più) del
codice compilato per la macchina reale (perché più veloce?).
© 2010 - CEFRIEL
Sintassi base

declarations:
– stessa sintassi del C: <tipo> <nome> [= valore];
• int pippo, pluto = 1;
float paperino = 1E-6f;
– non esistono i tipi senza segno (tipo “unsigned long”).

statements & constructs:
– stessa sintassi del C;
– assegnamenti:
<variabile> = <espressione>;
• senza puntatori gli L-values di un assegnamento possono essere
solo variabili, attributi o indicizzazione di array.
– costrutti di selezione:
• if(){}
if(){}else{}
– costrutti di iterazione:
• while(){}
– aggiunti:




switch(){case 1: default:}.
do{}while();
for(;;){}
• for (int elemento: arrayDiInteri) { /* Uso “elemento”. */ }
operators:
gli stessi del C.
tipi builtin:
boolean, char, short, int, long, float, double
modificatori: static, transient
boxing/unboxing:
– per ogni tipo builtin esiste una classe di boxing, istanziata in automatico
all’occorrenza e “spacchettata” automaticamente se serve;
– esempio: int  java.lang.Integer
© 2010 - CEFRIEL
Sintassi base

dotted notation:

reference-types VS value-types:
– dato un oggetto, gli attributi (campi) ed i metodi vengono identificati
come <oggetto>.<membro>;
– esempio: System.out.getClass().getName()
– ogni oggetto è un tipo riferimento, quindi:
• assegnamento  non clona ma copia il riferimento;
• side effects  possibili se l’oggetto viene passato;
– ogni tipo builtin è un valore, quindi:
• assegnamento per copia e side effects assenti.
– Simulare i value-types: oggetti immutabili  si evitano i side effects.

semantica del passaggio dei parametri:

operatori particolari:
– sempre per copia, ciò che viene copiato però può essere un riferimento,
consentendo quindi la modifica dell’oggetto da parte del metodo.
– new:
creazione di nuove istanze tramite costruttore:
• new String(123)  la classe String contiene un metodo
particolare capace di creare una stringa da un intero:
– public String(int num) {…}
– instanceof:
RTTI, Run-Time Type Identification:
• if (myVar instanceof MyClass)  controllo il tipo di “myVar”.
© 2010 - CEFRIEL
Sintassi base

dichiarazioni e gestione dello stack:
– nella JVM ogni elemento occupa una locazione dello stack (con
il proprio valore per i value-types, con il proprio riferimento
(puntatore) per i reference-types.
– un record di attivazione viene creato per ogni chiamata a
metodo, eventualmente crescendo e decrescendo durante
l’esecuzione (dichiarazioni in mezzo al codice).
– blocchi possono essere dichiarati per gestire la creazione e
distruzione parti di record di attivazione in mezzo a un metodo:
{ // inizio del blocco
// dichiarazioni nuove var. nel record di att.
} // fine del blocco

gestione della memoria:
– richiesta solo l’allocazione tramite costruttori e operatore “new”;
– garbage collector di tipo mark-and-sweep per la deallocazione.

strutture dati:
– array di tipi builtin e di oggetti;
• un array è un oggetto  array di array.
– classi, enum, annotazioni.
© 2010 - CEFRIEL
Classi

descrivono delle tipologie di “entità”:
– informazioni (attributi) che definiscono le caratteristiche di ogni istanza:
• esempio: class Colore { float red,green,blue; }
– operazioni (metodi) per manipolarle accedendo agli attributi:

• esempio, mescolare un colore in un altro:
•
class Colore {float red,green,blue; …
void mescola(Colore c) { red = red*0.5+c.red*0.5; … }
…}
•
Colore c = new Colore(0,0,1);
c.mescola(new Colore(1,0,0));
… nonché il loro concreto funzionamento:

possono essere utilizzate per realizzare:

che ruolo fargli avere sta a noi (vedasi nel Domain Driven Design).
– contengono la desc. di “come” un’entità si comporta (metodi astratti?);
– “cosa” un’entità è in grado di fare è meglio definirlo in un’interfaccia.
–
–
–
–
value objects: mantenendo l’immodificabilità degli attributi (immutables);
entities: evoluzione dello stato interno e mantenendo l’identità;
servizi: metodi che svolgono operazioni su richiesta, non su di sé;
factory: classi con lo scopo di creare istanze di altre classi (fabbriche).
© 2010 - CEFRIEL
Interfacce

descrivono solamente il contratto di “messaggistica”:
– gerarchia di tipi.. e non di classi/oggetti:
• utilizzata per il controllo di tipo a run-time;
– chi le implementa dichiara “cosa sa fare”:

• dichiarazione dei metodi implementati da una
determinata tipologia di classi;
• una classe può implementare più interfacce (simile
all’ereditarietà multipla).
separano behavior da implementation:
– rendono inconsapevole il client:
• utilizza un oggetto di natura ignota conoscendone
solamente le capacità o ruolo da esso coperto;
– permettono di definire punti di astrazione:
• non dipendendo dalla classe di implementazione,
si possono definire entità operanti su tipologie
astratte di entità distinte per le loro capacità.
© 2010 - CEFRIEL
Ereditarietà e implementazione

Ereditarietà (tra classi):
–
–
–
–
–
–
–

solamente singola in Java (no ereditarietà multipla);
è uno strumento di riutilizzo diretto del codice;
specializzazione del funzionamento di una classe;
astrazione tramite classi astratte e polimorfismo;
definisce la relazione “è un”;
principio di sostituibilità di Liskov;
occhio all’esempio classico “rettangolo ⊲⊦ quadrato”.
Implementazione (di interfacce):
– anche multipla e accoppiata all’ereditarietà;
– è uno strumento di astrazione e validazione a
compile-time (vedere prossimo punto);
– definisce la relazione “rispetta la specifica di un”.
© 2010 - CEFRIEL
Esempio fino a qui



Quali i vantaggi di quest’architettura?
Gli svantaggi? Astrazioni mancanti?
Realizziamolo e “giochiamoci”!
Value object
Servizio
Inconsapevole
«type»
Punto
-x : double
-y : double
+getX() : double
+getY() : double
+add(in p : Punto) : Punto
+mean(in p : Punto) : Punto
+dist(in p : Punto) : double
«interfaccia»
Terminale
+disegna(in p : Punto)
+fatto()
Client
Astrazione
Main
«classe implementazione»
TerminaleTestuale
Immutable
«instance»
«classe implementazione»
TerminaleGraficoAscii
Classi concrete
Mediator
«instance»
© 2010 - CEFRIEL
Enums

Cosa sono?
– entità esistenti in numero finito e noto a priori;
– spesso definiscono opzioni di funzionamento;
– possono rappresentare entità più complesse:

• unità di misura, costanti, comandi predefiniti,
descrizioni di pacchetti, …
Come funzionano:
– sono dichiarati similarmente a classi;
– prevedono una lista di chiamate ai propri costruttori;
– vengono istanziati (teoricamente) prima dell’esecuzione
dell’applicazione (sperimentare);
– NON possono essere istanziati esplicitamente;
– permettono una sintassi ricca:
• specificazione di attributi e metodi;
• specializzazione per singolo valore.
© 2010 - CEFRIEL
Annotations

Cosa sono?
– annotazioni allegate ad entità semantiche presenti nel codice
(tipi, attributi, metodi, parametri,…);
– possono trasportare informazione anche strutturata in maniera
complessa (un albero per tipo di annotazione);
– devono essere completamente definiti a compile-time;
– possono essere “osservati” a run-time (reflection);

Come funzionano?
– “non funzionano”, semplicemente ci sono, e offrono accesso ai
dati che contengono;
– chi li usa si aspetta che vengano “letti” da qualche altra parte del
SW, o da un framework utilizzato;
– chi li legge offre funzionalità dichiarative a chi li utilizza;
– consentono principalmente la metaprogrammazione;
– si analizzi JUnit come esempio interessante.
© 2010 - CEFRIEL
Gestione delle eccezioni

Prima delle eccezioni:
– [server] rilevato l’errore  restituito codice descrittivo;
– [client] controllato il codice  presi dei provvedimenti;
– un esempio:
• server:
• client:

Con le eccezioni:
se BAD allora restituisci ERR;
chiama server;
controlla codice di errore;
se ERR provvedi;
chiama server;
controlla codice di errore;
se ERR provvedi;
…
– [server] rilevato l’errore  generata eccezione da gestire;
– [client] svolge il proprio compito;
“cattura” le eccezioni che gli interessano e provvede;
“lascia passare” le altre così che qualcuno provveda.

Risultato: codice più pulito e comprensibile.
© 2010 - CEFRIEL
Generics

Problema di partenza:
– strutture dati internamente non tipizzate:
• utilizzo di “Object” come “void*” in C:
– nessun check di tipo effettuabile a compile-time;
• ADT semplici non (banalmente) definibili:
– Non basabili correttamente sulle API standard;
– impossibile propagare informazione di tipo tra strutture dati, se
non utilizzando la reflection;
• più lenta ed error-prone;
– valutata a run-time;
– richiede molti controlli per offrire robustezza;
• non validabile a run-time:
– con controlli del tipo “Mi dicono che classe vogliono, ma deve essere
per forza figlia di X perché il tutto funzioni.”;
– esplosione di errori a run-time evitabili tramite check automatico.

Soluzione: offrire uno strumento di manipolazione di tipi:
– funzionante (solo) a compile time (non modifica le performance);
– comprendente generalizzazione e vincoli verificabili dal
compilatore durante la generazione del codice.
© 2010 - CEFRIEL
Altro

Package:
– migliore granularità sulla visibilità;
– organizzazione gerarchica degli aspetti/funzionalità;
– ORDINE.. prima di tutto!

Classi anonime (innestate, …):
– permettono una immediata applicazione dell’abstract method
pattern (GoF, vedasi gestione eventi);
– riducono il tempo di sviluppo;
– MIGLIORANO la leggibilità del codice (se ben usate).

Collections e iteratori:
– concetti generali, entrati nella semantica.. e anche nella sintassi
del linguaggio (non solo comodità).

Varargs:
– utili.. ma solo per semplificare la scrittura del codice.
© 2010 - CEFRIEL
Esempio fino a qui

Per ripassare tramite esempi in Code\01_Syntax:
– EsempiSintassi\BaseSyntax.java
• sintassi di base del linguaggio.
–
–
–
–
EsempiSintassi\Exceptions.java
EsempiSintassi\Enums.java EsempiSintassi\Enums\*.java
EsempiSintassi\Generics.java
EsempiSintassi\Annotations.java
• sequenza consigliata per il ripasso.
– EsempioTerminali
• esempio di cui si sono mostrati i diagrammi UML.
– EsempioGruppi

• soluzione (parziale?) dell’esercizio seguente:
Costruire un package di manipolazioni di gruppi finiti (in senso
algebrico) di value-objects;
–
–
–
–
occhio a cosa il gruppo deve contenere;
modellare prima dal punto di vista astratto;
occhio alle operazioni astratte;
usare lo “zucchero sintattico” visto in precedenza.
© 2010 - CEFRIEL
Scarica

01_Syntax