Prerequisiti: per comprendere pienamente il contenuto di questa nota, oltre le conoscenze minime che do per scontato che tu sappia giΓ , ti consiglio di aver letto in precedenza queste altre note:
nome: di tipo String, ha visibilitΓ pubblica, quindi sarΓ visibile anche alle classi allβesterno del proprioΒ package.
razza: di tipo String, ha visibilitΓ privata, quindi sarΓ visibile solo allβinterno della stessa classe. Γ anche final, quindi Γ¨ una costante che puΓ² essere inizializzata una sola volta.
eta: di tipo int, ha visibilitΓ standard, quindi sarΓ visibile solo alle classi allβinterno del proprioΒ package. Inoltre, ha come valore iniziale 0: ciΓ² implica che, per ogni nuova istanza di questa classe appena creata, questo campo avrΓ automaticamente il valore 0.
Osservazione: differenza tra le struct di C e le classi di Java
Per certi versi, le struct di C possono sembrare molto simili alle classi in Java: entrambi rappresentano una collezione di variabili (che nel caso delle classi sono rappresentate dai campi), anche di tipo eterogeneo, ed entrambi possono essere usati come tipi di variabili.
Tuttavia, ci sono alcune importanti differenze tra questi due concetti:
Le classi possono contenere al loro interno anche dei metodi, mentre le struct possono contenere solo variabili.
Le struct sono sempre accessibili da qualsiasi punto del programma, mentre le classi possono avere una determinata visibilitΓ che le puΓ² nascondere ad alcuni pezzi del programma.
Β«NomeClasseΒ» Β«nome_istanzaΒ» = new Β«NomeClasseΒ»([Β«argomentiΒ»]);
dove:
Β«NomeClasseΒ»: nome della classe di cui vogliamo creare unβistanza.
Β«nome_istanzaΒ»: nome della variabile che conterrΓ lβistanza della classe.
[Β«argomentiΒ»] (opzionale): argomenti da passare al costruttore.
1.1 - Valori dei campi
Sintassi: riferimento a un campo di una classe in Java
In Java, la sintassi per riferirsi a un campo di una classe Γ¨ la seguente:
Β«nome_istanzaΒ».Β«nome_campoΒ»
dove:
Β«nome_istanzaΒ»: nome della variabile che contiene lβistanza della classe. Se ci si vuole riferire a un campo allβinterno della stessa classe in cui Γ¨ definito, allora si usa la parola chiave this.
Β«nome_campoΒ»: nome del campo della classe a cui ci si vuole riferire.
Questi campi possono essere considerati come delle variabili a tutti gli effetti, alle quali Γ¨ ovviamente possibile anche assegnare valori con la seguente sintassi:
Β«nome_istanzaΒ».Β«nome_campoΒ» = Β«valoreΒ»
1.2 - La parola chiave this
Definizione: parola chiave this
In Java, this Γ¨ una parola chiave riservata che rappresenta un riferimento implicito allβoggetto corrente, cioΓ¨ allβistanza della classe allβinterno della quale Γ¨ in esecuzione il codice, ed Γ¨ usato per accedere ai suoi campi e metodi.
2 - Metodi delle classi
Sintassi: definizione di un metodo in Java
In Java, la sintassi per definire un metodo Γ¨ la seguente:
[Β«visibilitΓ Β»] [Β«modificatoriΒ»] Β«tipoRestituitoΒ» Β«nomeMetodoΒ»(Β«parametriΒ») { Β«corpo del metodoΒ»}
dove:
[Β«visibilitΓ Β»] (opzionale): visibilitΓ del metodo. PuΓ² assumere uno dei seguenti valori:
synchronized: permette lβesecuzione del metodo su un solo thread alla volta negli ambienti multithread.
native: permette lβimplementazione del metodo usando codice nativo come C o C++.
strictfp: forza il rispetto degli standard IEEE 754 per i calcoli con la virgola mobile, assicurando la coerenza dei risultati su diverse piattaforme.
Β«tipoRestituitoΒ»: tipo del valore restituito dal metodo.
Β«nomeMetodoΒ»: nome del metodo, convenzionalmente in lowerCamelCase.
Β«parametriΒ»: elenco (eventualmente vuoto) dei parametri formali, ciascuno specificato come tipo nome.
Β«corpo del metodoΒ»: corpo del metodo. Se il metodo Γ¨ astratto, il corpo Γ¨ assente e la definizione termina con ;.
2.1 - Il metodo main e lβeseguibilitΓ delle classi
Definizione: metodo main
In Java, il main Γ¨ un metodo di una classe che rappresenta il punto dβingresso per lβesecuzione di un programma Java.
Il metodo main in Java Γ¨ molto simile alla funzione main in C: una classe Γ¨ eseguibile solo se contiene un metodo main, altrimenti puΓ² solo essere utilizzata come libreria da altre classi.
Il metodo main accetta un solo parametro: un array di elementi di tipo String (convenzionalmente scritto come String[] args).
2.2 - Metodi getter e setter
Proprio in virtΓΉ del concetto di incapsulamento, spesso Γ¨ bene rendere privati i campi di una classe per proteggerli da eventuali accessi esterni indesiderati: per questo, se si vuole offrire un accesso βcontrollatoβ a questi campi privati, bisogna che la classe fornisca dei metodi appositi per accedervi: i metodi getter e setter.
Definizione: metodi getter e setter
In Java, i getter e setter sono metodi pubblici usati rispettivamente per accedere e per modificare i campi privati di una classe.
2.3 - Overloading di metodi
In Java Γ¨ possibile definire piΓΉ versioni dello stesso metodo attraverso lβoverloading.
Definizione: overloading
In Java, lβoverloading Γ¨ il meccanismo tramite cui Γ¨ possibile definire piΓΉ versioni dello stesso metodo variando la sua firma, per esempio modificando il numero e il tipo dei parametri o la sua visibilitΓ . Le versioni multiple possono essere definite tutte allβinterno della stessa classe o anche in sottoclassi derivate.
3 - Costruttori
Le classi, oltre agli attributi (che in Java vengono chiamati campi per sottolineare la loro implementazione a livello di codice) e metodi, possono avere anche dei costruttori, che sono delle procedure speciali che restituiscono come risultato unβistanza della classe.
Definizione: costruttore
In Java, un costruttore Γ¨ una porzione di codice speciale che viene eseguita quando si crea unβistanza di una classe, ed Γ¨ usata per inizializzare lβoggetto. Presenta le seguenti caratteristiche:
Può essere sovraccaricato, cioè si possono definire più costruttori con parametri diversi.
Se non si definisce esplicitamente alcun costruttore, Java fornisce un costruttore di default (senza parametri).
Osservazione: un costruttore non Γ¨ un metodo
Sebbene comunemente venga confuso con un metodo, un costruttorenon Γ¨ un metodo, e trattarlo come tale puΓ² portare a errori concettuali. In particolare, non puΓ² essere ereditato da altri oggetti e non puΓ² essere chiamato direttamente come un metodo, ma Γ¨ strettamente vincolato alla creazione dellβoggetto.
Esempio di un costruttore
Ecco un esempio di una classePersona con costruttore che prende come parametri i valori dei due campi.
Persona.java
class Persona { String nome; int eta; // Costruttore public Persona(String nome, int eta) { this.nome = nome; this.eta = eta; } public static void main(String[] args) { Persona p = new Persona("Mario", 30); // Invocazione del nostro costruttore System.out.println("Nome: " + p.nome); System.out.println("EtΓ : " + p.eta); }}
Esempio di un costruttore di default
Ecco un esempio di una classePersona senza un costruttore esplicito, in cui viene invocato il costruttore di default che non accetta parametri e, di conseguenza, i valori vanno assegnati βmanualmenteβ ai campi.
Persona.java
class Persona { String nome; int eta; public static void main(String[] args) { Persona p = new Persona(); // Invocazione del costruttore di default p.nome = "Mario"; p.eta = 30; System.out.println("Nome: " + p.nome); System.out.println("EtΓ : " + p.eta); }}
Attenzione: costruttori espliciti sovrascrivono automaticamente quello di default
class Persona { String nome; int eta; // Costruttore public Persona(String nome, int eta) { this.nome = nome; this.eta = eta; } public static void main(String[] args) { Persona p = new Persona("Mario", 30); // Invocazione del nostro costruttore System.out.println("Nome: " + p.nome); System.out.println("EtΓ : " + p.eta); Persona q = new Persona(); // Invocazione del costruttore di default }}
In Java, un package Γ¨ un nome di spazio che identifica un insieme coerente di classi e interfacce, organizzandole in modo ordinato per migliorarne la modularitΓ , la leggibilitΓ , e per evitare conflitti di nomi.
Concettualmente, puoi pensare ai package come a cartelle diverse sul tuo computer.
4.1 - VisibilitΓ delle classi
Ogni file puΓ² contenere una o piΓΉ classi, ognuna delle quali ha una propria visibilitΓ .
Definizione: visibilitΓ di una classe
In Java, la visibilitΓ di una classe indica la proprietΓ di una classe di poter essere visibile da determinate altre classi. PuΓ² essere di due tipi:
Privata: la classe Γ¨ visibile solo alle classi allβinterno dello stesso package in cui Γ¨ stata definita.
Pubblica: la classe Γ¨ visibile anche alle classi allβesterno del package in cui Γ¨ stata definita, previo lβutilizzo di istruzioni di import, o assegnazione delle variabili dβambiente, o di opportuni parametri dati al compilatore.
4.2 - VisibilitΓ dei campi
Definizione: visibilitΓ di un campo
In Java, la visibilitΓ di un campo indica la proprietΓ di un campo di poter essere visibile da determinate altre classi. PuΓ² essere di quattro tipi:
Default (anche detta package): il campo Γ¨ visibile solo a classi dello stesso package in cui Γ¨ stato definito.
Privata: il campo Γ¨ visibile solo allβinterno della stessa classe in cui Γ¨ stato definito.
Protetta: il campo Γ¨ visibile solo a classi dello stesso package in cui Γ¨ stato definito e anche a sottoclassi in altri package.
Pubblica: il campo Γ¨ visibile anche alle classi allβesterno del package in cui Γ¨ stato definito.
4.3 - VisibilitΓ dei metodi
Definizione: visibilitΓ di un metodo
In Java, la visibilitΓ di un metodo indica la proprietΓ di un metodo di poter essere visibile da determinate altre classi. PuΓ² essere di quattro tipi:
Default (anche detta package): il metodo Γ¨ visibile solo a classi dello stesso package in cui Γ¨ stato definito.
Privata: il metodo Γ¨ visibile solo allβinterno della stessa classe in cui Γ¨ stato definito.
Protetta: il metodo Γ¨ visibile solo a classi dello stesso package in cui Γ¨ stato definito e anche a sottoclassi in altri package.
Pubblica: il metodo Γ¨ visibile anche alle classi allβesterno del package in cui Γ¨ stato definito.
4.4 - VisibilitΓ dei costruttori
Definizione: visibilitΓ di un costruttore
In Java, la visibilitΓ di un costruttore indica la proprietΓ di un costruttore di poter essere visibile da determinate altre classi. PuΓ² essere di quattro tipi:
Default (anche detta package): il costruttore Γ¨ visibile solo a classi dello stesso package in cui Γ¨ stato definito.
Privata: il costruttore Γ¨ visibile solo allβinterno della stessa classe in cui Γ¨ stato definito.
Protetta: il costruttore Γ¨ visibile solo a classi dello stesso package in cui Γ¨ stato definito e anche a sottoclassi in altri package.
Pubblica: il costruttore Γ¨ visibile anche alle classi allβesterno del package in cui Γ¨ stato definito.
Β«NomeClasseΒ»: nome della classe, convenzionalmente scritto in UpperCamelCase.
Β«NomeSuperclasseΒ»: nome della superclasse da cui la classe deve ereditare metodi e campi.
Β«contenuto della classeΒ»: campi e metodi della classe.
Allβinterno del costruttore della sottoclasse, la prima istruzione a dover essere eseguita devβessere lβistruzione super(Β«parametriΒ»), dove i Β«parametriΒ» sono i parametri del costruttore della superclasse che si vuole usare.
LβobbligatorietΓ di invocare e eseguire un costruttore della superclasse Γ¨ data dal fatto che la responsabilitΓ di inizializzare i campi della superclasse Γ¨ della superclasse stessa.
In Java Γ¨ possibile definire sovrascrivere un metodo con una sua versione modificata nelle sottoclassi attraverso lβoverriding.
Definizione: overriding
In Java, lβoverriding Γ¨ il meccanismo tramite cui Γ¨ possibile sovrascrivere un metodo in sottoclassi derivate modificando il suo corpo.
Attenzione: non confondere overriding e overloading!
Fai attenzione a non confondere lβoverriding con lβoverloading! Mentre con lβoverriding il metodo nella superclasse viene completamente sostituito da quello nella sottoclasse che ha la stessa firma ma corpo diverso, nellβoverloading lo stesso metodo ha molteplici versioni ognuna delle quali con una firma e un corpo diversi.
6 - La classe Object
Definizione: classe Object
In Java, Object Γ¨ la superclasse di tutte le classi: ogni classe in Java deriva implicitamente da Object.
Si trova nella libreria java.lang, per questo non va importata.
La classe Object contiene al suo interno alcuni metodi che vengono automaticamente ereditati da tutte le classi in Java.
6.1 - Il metodo toString
Definizione: metodo toString
In Java, toString Γ¨ un metodopubblicodinamico che appartiene alla classe Object, ha tipo di ritorno String e nessun parametro. Restituisce una rappresentazione testuale dellβoggetto su cui viene chiamato, stampando lβindirizzo in memoria (in esadecimale) dellβoggetto:
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode());}
Osservazione: sovrascrittura del metodo toString
Come ogni altro metodo ereditato da una superclasse, anche il metodo toString puΓ² venire sovrascritto per costruire una stringa diversa, per esempio per fare il βpretty printingβ dei valori dei campi degli oggetti.
6.2 - Il metodo equals e lβuguaglianza tra oggetti
Definizione: metodo equals
In Java, equals Γ¨ un metodopubblicodinamico che appartiene alla classe Object, ha tipo di ritorno boolean e come parametro un oggettoobj di tipo Object. Restituisce true se lβoggetto puntato dalla parola chiave this e quello puntato da obj corrispondono allo stesso indirizzo, false altrimenti:
public boolean equals(Object obj) { return (this == obj);}
Β«NomeClasseΒ»: nome della classe, convenzionalmente scritto in UpperCamelCase.
Β«contenuto della classeΒ»: campi e metodi della classe.
Definizione: metodo astratto
In Java, un metodo astratto Γ¨ un metodo dichiarato in una classe astratta con la parola chiave abstract e con il corpo vuoto. Esso rappresenta un comportamento che le sottoclassi non astratte sono obbligate a definire sovrascrivendolo.
In Java, unβinterfaccia Γ¨ una collezione di variabili e di metodi astratti, statici o default, con visibilitΓ pubblica o privata che possono essere implementati da una o piΓΉ classi. Serve a definire un comportamento comune, senza imporre una struttura gerarchica rigida come nelle classi astratte.
Osservazione: differenze tra interfacce e classi astratte
Una classe qualsiasi puΓ² ereditare al piΓΉ da una sola classe (astratta o no), ma puΓ² implementare un numero qualunque di interfacce.
9 - Classi generiche
PuΓ² capitare, in Java, che esistano copie di alcuni metodi che si comportano allo stesso modo ma operano su diversi tipi di attributi. Per esempio, un metodo sort() che ordina un array di elementi potrebbe avere due versioni:
Una versione che ordina gli array di stringhe in ordine alfabetico.
Una versione che ordina gli array di interi in ordine crescente.
Una classe Γ¨ detta generica (o parametrica) se contiene nella sua definizione variabili di tipo, ossia delle variabili che non contengono al loro interno dei valori ma che sono dei placeholder per tipi di dato derivati.
Β«NomeClasseΒ»: nome della classe, convenzionalmente scritto in UpperCamelCase.
Β«variabili di tipoΒ»: nomi delle variabili che verranno usati come placeholder per i tipi generici.
Β«contenuto della classeΒ»: campi e metodi della classe, possono usare le variabili di tipo specificate in Β«variabili di tipoΒ» come placeholder per i tipi di campi e variabili.
Β«NomeClasseΒ»<Β«variabili di tipoΒ»> Β«nome_istanzaΒ» = new Β«NomeClasseΒ»<Β«variabili di tipoΒ»>([Β«argomentiΒ»]);
dove:
Β«NomeClasseΒ»: nome della classe di cui vogliamo creare unβistanza.
Β«variabili di tipoΒ»: nomi delle variabili che verranno usati come placeholder per i tipi generici.
Β«nome_istanzaΒ»: nome della variabile che conterrΓ lβistanza della classe.
[Β«argomentiΒ»] (opzionale): argomenti da passare al costruttore.
Vediamo un esempio di definizione e istanziazione di una classe generica.
Esempio: definizione e istanziazione di una classe generica Coppia
Ecco un esempio di una classe genericapubblica di nome Coppia che ha due variabili di tipo T ed S.
public class Coppia<T, S> { private T first; private S second; public Coppia(T first, S second) { this.first = first; this.second = second; } public T getFirst() { return first; } public void setFirst(T first) { this.first = first; } public S getSecond() { return second; } public void setSecond(S second) { this.second = second; }}
Due campiprivati, first e second, rispettivamente di tipo T ed S. Al momento della dichiarazione della classe, non si sa ancora che tipi saranno T ed S, questi sono semplici placeholder per i tipi veri e propri che verranno usati nellβesecuzione del metodo main.
Un costruttore che ha come parametri i valori da assegnare inizialmente ai campifirst e second.
Sintassi: istanziazione di una classe generica in Java con la diamond notation
In Java, nellβistanziazione di una classe generica, Γ¨ possibile evitare di definire esplicitamente le variabili di tipo non inserendo nulla allβinterno delle parentesi angolate <> (che sembra appunto il simbolo di un diamante, da cui il nome della notazione):
Β«NomeClasseΒ»<> Β«nome_istanzaΒ» = new Β«NomeClasseΒ»<>([Β«argomentiΒ»]);
dove:
Β«NomeClasseΒ»: nome della classe di cui vogliamo creare unβistanza.
Β«nome_istanzaΒ»: nome della variabile che conterrΓ lβistanza della classe.
[Β«argomentiΒ»] (opzionale): argomenti da passare al costruttore.
In questo modo, il compilatore capirΓ in automatico dagli argomenti passati al costruttore quali sono le variabili di tipo.
In pratica, si utilizza la classe come se non fosse generica, il che disattiva parte della sicurezza offerta da questo tipo di classi. Vediamo un esempio.
Esempio: istanziazione della classe generica Coppia come raw type
I raw type esistono per motivi compatibilità con codice precedente a Java 5, cioè prima che le classi generiche venissero introdotte. Non dovrebbero essere usati nel codice moderno a meno che non sia assolutamente necessario (es. interazione con librerie legacy).
9.2 - Classi wrapper
Una difficoltΓ nellβuso delle classi generiche Γ¨ che Java considera una classe come un insieme di indirizzi di dati: in base a questa definizione, i tipi primitivi (come int, bool, double, ecc.) non sono classi e non possono quindi essere sostituiti alle variabili di tipo.
Per ovviare a questo problema, Java ci fornisce le classi wrapper.
Definizione: classe wrapper
Un wrapper Γ¨ una classe composta da un indirizzo che punta a un oggetto contenente un dato di tipo primitivo.
π« Lezioni e slide della Prof. Bono Viviana del corso di Principi di Programmazione Orientata agli Oggetti (canale B), Corso di Laurea in Informatica presso lβUniversitΓ di Torino, A.A. 2024-25: