Alcune parole chiave della programmazione a oggetti (ordine sparso)

Wrapper

Un wrapper è un oggetto che rappresenta un tipo primitivo.

Per esempio, Float è il wrapper di float

Un wrapper contiene, a differenza di un tipo primitivo, dei metodi, per esempio per convertire una stringa in numero (parse, parseTo).

Autoboxing

L'Autoboxing è la capacità del compilatore di convertire automaticamente oggetti wrapper nei corrispondenti tipi nativi e viceversa.

Per esempio, il codice

Float f = 3.2;

è perfettamente valido perché 3.2 è convertito in un oggetto Float il cui valore è 3.2.

Casting

Il casting è l'operazione di modifica di un tipo di un oggetto con un altro tipo compatibile.

Per esempio, il seguente codice fa un ciclo in un elenco di figure, e se trova un cerchio ne calcola il raggio.

for(Shape s: shapes) {
    if(s instanceof Circle) {
        return ((Circle)s).getRadius();
    }
}

Il ciclo deve essere effettuato su oggetti di tipo Shape, ma la proprietà radius è presente solo nei cerchi.
Il cast si ottiene anteponendo il nuovo tipo tra parentesi.

int i = 5;
float f = (float)i;

Metodo

Un metodo è una serie di istruzioni disponibili in un oggetto.
Descrivendolo in modo intuitivo, il metodo è ciò che l'oggetto fa.

  • Può essere private, protected o public.
  • Può essere static, se non richiede che l'oggetto sia istanziato.
  • Può essere final/sealed; in questo caso non è ridefinibile (vedi overload).
  • La sintassi richiede che sia sempre specificato cosa restituisce: un oggetto, un tipo primitivo oppure void (niente).
  • Può prendere parametri.

L'insieme degli elementi dell'elenco si chiama firma dell'oggetto.

Override/sovrascrittura

L'override è la riscrittura di un metodo ereditato.

Per esempio

public class Shape {
    @Override
    public String toString() {return "Questa è una figura geometrica";}
}
public class Rectangle extends Shape {
    @Override
    public String toString() {return "Questo è un rettangolo";}
}

richiamando toString() in un oggetto di tipo Rectangle si ottiene "Questo è un rettangolo".

Overload/sovraccarico (polimorfismo)

L'overload consiste nella possibilità di utilizzare lo stesso metodo con parametri diversi.

class Rectangle {
    public Rectangle(float top, float left, float width, float height) {
        ...
    }
    public Rectangle(float top, float left, float side) {
        ... // disegna un quadrato
    }
}

Varargs

Si usa per specificare un numero non conosciuto a priori di parametri.

public String concat(String... list) {
    String appo = "";
    for(String s: list) appo += s;
    return appo;
}

questa procedura prende un numero indeterminato di stringhe e le concatena

Instanza

Una classe è la definizione di una serie di caratteristiche di un oggetto. Un'istanza è l'insieme dei valori di queste caratteristiche per uno degli oggetti.

Un oggetto si istanzia con la parola chiave new e di seguito uno dei costruttori della classe.

Costruttore

Il costruttore di un oggetto è un metodo particolare che ha lo stesso nome della classe e non ha valori di ritorno (non è specificato nemmeno void).

Il costruttore è un metodo eseguito automaticamente quando si istanzia una classe.

I costruttori possono avere firme diverse, cioè prendere parametri diversi, come i metodi normali (a differenza dei metodi normali, che ritornano un tipo, i costruttori si differenziano solo per i parametri passati).

this

Rappresenta l'istanza corrente.

Di solito è utilizzato per fare riferimento all'istanza stessa, per esempio quando va passata a un metodo come parametro, oppure nel caso in cui ci sia ambiguità nei nomi di variabile.

class Example {
    int valore;
    public Example(int valore) {
        this.valore = valore; // il primo è la proprietà della classe, il secondo è il nome della variabile
    }
}

super

Rappresenta la classe superiore di una classe che eredita.

static

Definisce che una classe è statica.

Una classe statica non può essere istanziata, e quindi i metodi e le proprietà che ne fanno parte possono essere richiamate senza fare uso di new.

class Example {
    public static String myUpper(String lower) {
        return String.toUpper(lower);
    }
    public Example() {
        String s = "ciao";
        System.out.println(Example.myUpper(s));
    }
}

Array

Gli array sono strutture dati che contengono molti valori dello stesso tipo. Il numero di elementi deve essere dichiarato all'inizio e non può essere cambiato.

Gli array sono indicizzati con numeri che vanno da 0 (zero) a n-1, dove n è il numero di elementi. Quindi un array di 10 elementi avrà come indici 0,1,2,3,4,5,6,7,8,9.

L'indice di un array deve essere compreso tra parentesi quadre:

int[] elenco = new int[10];
elenco[3] = 18 // il quarto elemento

enum

La parola chiave enum permette di creare delle variabili che contengono uno di un elenco di valori fissi.

public class Example {
    public enum weekDays = {LUN, MAR, MER, GIO, VEN, SAB, DOM};
    public weekDays today;
    public Example(weekDays d) {
        this.today = d;
    }
    public Example() {
        this.today = weekDays.DOM; // se non specifico diversamente, è domenica
    }
}

Classe astratta

Una classe astratta è un tipo di classe che non può essere istanziata. L'unico modo di utilizzare una classe astratta è specializzarla in una classe non astratta.

In una classe astratta alcuni metodi possono riportare la sola firma; poi nella classe specializzata il metodo viene definito. In questo modo la classe astratta garantisce la presenza di un metodo con quella determinata firma, ma lascia il programmatore libero di definire per ogni sottoclasse il comportamento desiderato.

abstract class Animale {
    int numeroZampe;
    public void corri();
}

class Cane extends Animale {
    public Cane() {
        numeroZampe = 4; // numeroZampe è ereditato dalla superclasse
    }
    public void corri() {
        // implementazione della corsa a quattro zampe
    }
}

class Umano extends Animale {
    public Umano() {numeroZampe = 2;}
    public void corri() {
        // implementazione della corsa a due zampe
    }
    public void scrivi(String s) {
        // la classe specializzata può aggiungere metodi propri
    }
}

Interfaccia

Un'interfaccia definisce proprietà e metodi di una classe, senza però implementarne i metodi.

Nei linguaggi senza ereditarietà multipla, le interfacce sono utili per implementare in oggetti diversi - magari che ereditano già da una classe - alcune caratteristiche.

class Book extends Sellable implements Readable

in questo esempio definiamo una classe Book come un oggetto che eredita da una superclasse Sellable (vendibile) che implementa l'interfaccia Readable (leggibile).

Classe ospite

Una classe ospite è una classe definita dentro un'altra classe (solitamente le classi sono definite in file che portano lo stesso nome della classe più l'estensione .java).

Una classe ospite - se non static - può accedere alle proprietà e ai metodi della classe ospitante, anche se private.

Classe anonima

Una classe anonima è un'istanza di una classe non definita (es. un'interfaccia o una classe astratta) che, per un uso locale, definisce "al volo" tutti i metodi necessari.

public interface ICiao {
    public String saluta(String nome);
}

public class Example {
    public Example() {
        String nome = "Antonio";
        ICiao saluto = new ICiao(nome) {
            @Override
            public String saluta(String nome) {
                System.out.println("Saluta "+nome);
            }
        }
    }
}

ovviamente la classe istanziata non ha un tipo definito (è anonima) e non avendo una definizione se non quella data "al volo" non può essere usata fuori dal metodo che la definisce.

Una classe anonima può accedere a tutti i membri della classe che la contiene, anche a quelli privati.

Le classi anonime sono molto utilizzate in contesti in cui è frequente la gestione degli eventi (handlers).

Generics

I tipi generici sono tipi definiti successivamente dalla classe che li utilizza.

Un esempio tipico di generics sono quelli usati nelle liste

// ArrayList<T>
ArrayList<String> ListaDiStringhe = new ArrayList<>();
ArrayList<MyObject> ListaDiMyObject = new ArrayList<>();

Un parametro generic può essere limitato anche a tipi che soddisfino alcune condizioni, come ereditare da un'altra classe o da un'interfaccia

// MyArrayList<T extends MyInterface>
ArrayList<MyTypeExtendingMyInterface1> uno;
ArrayList<MyTypeExtendingMyInterface2> due;

enhanced for

La sintassi "migliorata" del ciclo for consiste in for(tipo istanza: collezione)

Questo:

for(int i = 0; i < collezione.count(); i++)
    myObject o = collezione.get(i);

diventa:

for(myObject o: collezione)

differenze tra collezioni (collection) e insiemi (set) e mappe (map)

collezione set map
Valori duplicati no sì (valori, ma non chiavi)
Elementi ordinati non tutti sì, per chiave

map

map è una classe astratta che utilizza chiave e valore.

Esistono varie specializzazioni di map, tra cui:

  • HashMap (consente valori null)
  • HashTable (non consente valori null)
  • TreeMap (consente ordinamento per chiave)

Gli elementi sono gestiti tramite i metodi get() e put()

lambda expression

Le espressioni lambda permettono di scrivere codice più sintetico. Si può sostituire il codice in questa maniera, nel caso in cui sia definita un'interfaccia myInterface con un metodo myMethod().

  1. creare una nuova classe basata sull'interfaccia myInterface, implementare il metodo myMethod; creare un'istanza dell'oggetto e poi richiamare myMethod
public interface myInterface {
    public int myMethod(int a, int b);
}
public myClass implements myInterface {
    @Override
    public int myMethod(int a, int b) {return a+b;}
}
public static void run() {
    ArrayList<myInterface> myArrayList = new ArrayList<>();
    myClass c = new myClass();
    myArrayList.add(c);
}
  1. utilizzare direttamente l'interfaccia tramite una classe astratta: si richiama new myInterface() e si implementa direttamente dentro il new il metodo myMethod
public interface myInterface {
    public int myMethod(int a, int b);
}
public static void run() {
    ArrayList<myInterface> myArrayList = new ArrayList<>();
    myArrayList.add(new myInterface() {
        public int myMethod(int a, int b) {return a+b};
    }.myMethod(a, b));
}
  1. utilizzare una lambda expression al posto dell'implementazione
public interface myInterface {
    public int myMethod(int a, int b);
}

public static void run() {
    ArrayList<myInterface> myArrayList = new ArrayList<>();
    myArrayList.add((int a, int b) -> { return a+b; });
}

stream

Uno stream è un oggetto che rappresenta una sequenza di elementi, su cui è possibile fare delle operazioni di ricerca, filtro e ordinamento.

Sono utilizzati molto in accoppiata con le lambda expression.

myList.stream()
    .filter((a) -> a.value < 100)
    .sort()
    .forEach((a) -> System.out.println(a.value));

Classe immutabile

Una classe immutabile è una classe i cui elementi non cambiano dopo l'inizializzazione.

Tutti i campi sono dichiarati final, così come la classe, e non ci sono modificatori (es. setter).