Rubrica con Contatti, ereditarità ed arraylist in Java

Un esercizio Java interessante che unisce la gestione dei Contatti telefonici di una rubrica tramite ArrayList e classe dedicata. Sfruttiamo elementi di ereditarietà per personalizzare i contatti Aziendali e vediamo come Java abbia anche una forma di gestione automatica del polimorfismo dopo aver visto un esempi o di uso di instanceof qui. Lo schema che descrive la specifica è il seguente.

Schema della Classi

La specifica è abbastanza intuitiva:

Realizzare la classe Contatto aggiungendo costruttori come in schema con costruttore vuoto e di copia; metodi di accesso set/get ed una stampa dei valori. Similmente per ContattoAziendale. La classe Rubrica contiene un opportuni arraylist di contatti con le funzion base stampaContatti, inserimento di un contatto, cancellazione di un contatto nota la sua posizione/indice dell’arraylist, una funzione cerca per nome che restituisce l’intero oggetto o analogamente la sua posizione. Creare una classe Main per testare come blackbox il codice inserendo un congruo numero di Contatti nell’arraylist e verificando l’esito dei singoli metodi implementati. La tipologia dei campi tipo* è realizzata con un semplice enum da precedere in una delle classi opportune.

Classe Contatto

enum Tipo  {NessunaEtichetta, Cellulare, Lavoro, Principale};

public class Contatto
{
    // ATTRIBUTI
    private String nome;
    private String cognome;
    private String telefono;
    private Tipo tipotelfono;

    // COSTRUTTORI
    public Contatto(){}

    public Contatto(String nome, String cognome, String telefono, Tipo tipotelfono)
    {
        this.nome = nome;
        this.cognome = cognome;
        this.telefono = telefono;
        this.tipotelfono = tipotelfono;
    }

    public Contatto(Contatto c)
    {
        this.nome = c.getNome();
        this.cognome = c.getCognome();
        this.telefono = c.getTelefono();
        this.tipotelfono = c.getTipotelfono();
    }


    // METODI DI ACCESSO
    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getCognome() {
        return cognome;
    }

    public void setCognome(String cognome) {
        this.cognome = cognome;
    }

    public String getTelefono() {
        return telefono;
    }

    public void setTelefono(String telefono) {
        this.telefono = telefono;
    }

    public Tipo getTipotelfono() {
        return tipotelfono;
    }

    public void setTipotelfono(Tipo tipotelfono) {
        this.tipotelfono = tipotelfono;
    }


    // METODI ACCESSORI
    void stampaInfo()
    {
        System.out.println("|---------------------------------------|");
        System.out.println("|               CONTATTO                |");
        System.out.println("|---------------------------------------|");
        System.out.println("| Nome            : " + String.format("%-20s", this.nome) + "|");
        System.out.println("| Cognome         : " + String.format("%-20s", this.cognome) + "|");
        System.out.println("| Telefono        : " + String.format("%-20s", this.telefono) + "|");
        System.out.println("| Tipo            : " + String.format("%-20s", this.tipotelfono) + "|");
        System.out.println("|---------------------------------------|");
    }
}

Classe ContattoAziendale

La classe non presenta particolari criticità se non l’utilizzo ponderato della parola chiave super() nei costruttori per ereditare i costruttori della classe padre. Occhio ad inserire la dicitura extends in alto. Qui abbiamo deciso di mettere gli attributi del padre come privati quindi nel figlio sono presenti ma accessibili solo con i metodi set/get.

public class ContattoAziendale extends Contatto
{
    // ATTRIBUTI
    private String societa;
    private String mail;
    private Tipo tipoemail;

    // COSTRUTTORI
    public ContattoAziendale()
    {
        super();
    }

    public ContattoAziendale(String nome, String cognome, String telefono, Tipo tipotelfono, String societa, String mail, Tipo tipoemail) {
        super(nome, cognome, telefono, tipotelfono);
        this.societa = societa;
        this.mail = mail;
        this.tipoemail = tipoemail;
    }

    public String getSocieta() {
        return societa;
    }

    public void setSocieta(String societa) {
        this.societa = societa;
    }

    public String getMail() {
        return mail;
    }

    public void setMail(String mail) {
        this.mail = mail;
    }

    public Tipo getTipoemail() {
        return tipoemail;
    }

    public void setTipoemail(Tipo tipoemail) {
        this.tipoemail = tipoemail;
    }

    void stampaInfo()
    {
        System.out.println("|---------------------------------------|");
        System.out.println("|         CONTATTO AZIENDALE            |");
        System.out.println("|---------------------------------------|");
        System.out.println("| Nome            : " + String.format("%-20s", this.getNome()) + "|");
        System.out.println("| Cognome         : " + String.format("%-20s", this.getCognome()) + "|");
        System.out.println("| Telefono        : " + String.format("%-20s", this.getTelefono()) + "|");
        System.out.println("| Tipo            : " + String.format("%-20s", this.getTipotelfono()) + "|");
        System.out.println("| Società         : " + String.format("%-20s", this.societa) + "|");
        System.out.println("| E-Mail          : " + String.format("%-20s", this.mail) + "|");
        System.out.println("| Tipo            : " + String.format("%-20s", this.tipoemail) + "|");
        System.out.println("|---------------------------------------|");
    }
}

Classe Rubrica

La classe più interessante dell’esercizio probabilmente. Tralasciamo l’attributo proprietario che è semplicissimo da gestire con costruttori e metodi di accesso, concentriamoci sull’ArrayList. In fase di dichiarazione degli attributi, va dichiarato il tipo contenuto nell’arraylist, quindi è obbligatorio inserire la dicitura private ArrayList<Contatto> lista; dove lista è il nome arbitrario della variabile. Essendo l’arraylist un tipo complesso da gestire, andrebbe dichiarato ArrayList<Contatto> lista = new ArrayList<>(); ma non possiamo farlo tutto nella parte degli attributi. L’inizializzazione col new va fatta per forza nei costruttori, tutti, anche quello vuoto.

Qui ci sono due scelte: o specifichiamo new ArrayList<>() o new ArrayList<Contatto>(). Col secondo, in caso di ereditarietà, la gestione del polimorfismo va fatta a mano, utilizzando instanceof per capire quale tipo di classe padre o figlio invoca un determinato metodo in overriding. Il secondo invece torna comodo, a discapito delle prestazioni magari, ma auto chiama il metodo della classe padre/figlio a seconda che ad invocarlo sia un oggetto di un tipo piuttosto che l’altro. Molto comodo per lo sviluppatore, meno didattico per lo studente.

Per i metodi, inserimento e cancellazione in base alla posizione non presentano particolari sorprese. Abbiamo inserito il costrutto try/catch per gestire eventuali errori. Non è obbligatorio, ma rimane una prassi consolidata tra il software ben scritto.

La stampaContatti fa leva dell’appena citato polimorfismo automatico, che chiama la stampaInfo() di Contatto o ContattoAziendale.

La ricerca l’abbiamo implementata in due modi, sempre filtrando per nome. Il lettore può aggiungere gli analoghi col cognome o altri filtri. Abbiamo fatto due versioni: una restituisce un oggetto Contatto che possiamo quindi modificare o utilizzare in modo complesso, un altro cerca restituisce la posizione del record trovato, -1 se non lo trovo o null se non c’è oggetto Contatto da far tornare.

import java.util.ArrayList;

public class Rubrica
{
    // ATTRIBUTI
    private String proprietario;
    private ArrayList<Contatto> lista;

    // COSTRUTTORI
    public Rubrica()
    {
        this.lista = new ArrayList<>(); //se non rispecifico il tipo Contatto usa polimorfismo in automatico
    }

    public Rubrica(String proprietario)
    {
        this(); //richiama il costruttore vuoto
        this.proprietario = proprietario;
    }


    // MEDOTI DI ACCESSO

    public String getProprietario() {
        return proprietario;
    }

    public void setProprietario(String proprietario) {
        this.proprietario = proprietario;
    }


    // METODI ACCESSORI
    public void inserisciContatto(Contatto _c)
    {
        try
        {
            this.lista.add(_c);
        }
        catch(Exception e)
        {
            System.out.println("ERRORE DI INSERIMENTO");
        }

    }

    public Contatto cercaContattoPerNome(String _nome)
    {
        for (int i=0; i < lista.size(); i++)
        {
            if (lista.get(i).getNome() == _nome)
            {
                return lista.get(i);
            }
        }

        return null;
    }

    public int cercaPosizioneContattoPerNome(String _nome)
    {
        for (int i=0; i < lista.size(); i++)
        {
            if (lista.get(i).getNome() == _nome)
            {
                return i;
            }
        }

        return -1;
    }

    public void cancellaContatto(int _posizione)
    {
        try
        {
            this.lista.remove(_posizione);
        }
        catch(Exception e)
        {
            System.out.println("ERRORE DI CANCELLAZIONE");
        }
    }

    public void stampaContatti()
    {
        for (int i=0; i < lista.size(); i++)
        {
            lista.get(i).stampaInfo();
        }
    }
}

Listati codice

Trovate il codice completo su GitHub -> qui

Ultima modifica 12 Gennaio 2023