Classe conto corrente con gestione banca e arraylist ed ereditarietà

Vediamo un esercizio java con diversi spunti di riflessione e proprietà del nostro linguaggio. Implementiamo una classe che descrive una banca in cui è possibile aprire più conti corrente. La banca archivia questi conti con un comodo ArraList. Il conto corrente è invece una realtà duplice: c’è una classe padre e due figli che la specilizzano ereditando, uno sarà il conto corrente per le persone fisiche private, uno giuridico per le aziende. Lo schema UML di quello che vogliamo catalogare è riportato in basso. Anche se non specificati per brevità, sono da implementare i metodi di accesso, costruttori e costruttori di copia. Dei metodi da costruire si sappia che:

  • inserisciConto aggiunge all’arraylist l’oggetto del parametro
  • cancellaConto elimina un oggetto ContoCorrente dall’ArrayList cercandolo per IBAN passato come parametro
  • cercaConto esegue la ricerca per nome e cognome se l’istanza è di ContoCorrentePrivato, per piva e ragioneSociale se ContoCorrenteGiuridico (DIFFICILE)
  • stampaInfoConti gira tutto l’arraylist stampando le info dei conti presenti
  • totaleSaldi da la somma dei saldi di tutti i conti dell’arraylist
  • versamento aggiunge il double in parametro al saldo dell’oggetto e aumenta il numero di conteggioMovimenti di 1
  • prelievo sottrae se possibile il double in parametro al saldo dell’oggetto e aumenta il numero di conteggioMovimenti di 1
  • stampaInfo è in ovverride stampando le informazioni delle rispettive classi figlio + padre

Classe ContoCorrente e figli

La classe conto corrente non presenta particolari difficoltà realizzative. Tre attributi di cui generare i metodi di accesso e i costruttori. Coem sempre, al di la della specifica, conviene in overloading creare un costruttore vuoto da rispecificare deliberatamente, un costruttore con i tre parametri corrispondenti gli attributi e il costruttore di copia. I metodi di versamento e prelievo sono piuttosto intuitivi: uno aggiunge l’altro sottrae all’attributo saldo la quantità del parametro. Unico controllo che possiamo fare è sul prelievo da consentire solo se il saldo lo consente numericamente per non andare in negativo. Ogni volta che eseguiamo uno di questi metodi, aumentiamo anche l’attributo del conteggioMoviementi di uno. La stampaInfo, è una classica visualizzazione a schermo della situazione degli attributi.

public class ContoCorrente
{
    private String IBAN;
    private double saldo;
    private int conteggioMovimenti;

    //COSTRUTTORI

    public ContoCorrente()
    {
    }

    public ContoCorrente(String IBAN, double saldo, int conteggioMovimenti)
    {
        this.IBAN = IBAN;
        this.saldo = saldo;
        this.conteggioMovimenti = conteggioMovimenti;
    }

    public ContoCorrente(ContoCorrente _c)
    {
        this.IBAN = _c.getIBAN();
        this.saldo = _c.getSaldo();
        this.conteggioMovimenti = _c.getConteggioMovimenti();
    }

    //METODI ACCESSORI

    public String getIBAN() {
        return IBAN;
    }

    public void setIBAN(String IBAN) {
        this.IBAN = IBAN;
    }

    public double getSaldo() {
        return saldo;
    }

    public void setSaldo(double saldo) {
        this.saldo = saldo;
    }

    public int getConteggioMovimenti() {
        return conteggioMovimenti;
    }

    public void setConteggioMovimenti(int conteggioMovimenti) {
        this.conteggioMovimenti = conteggioMovimenti;
    }

    //METODI ACCESSORI
    public void versamento(double _soldi)
    {
        this.saldo += _soldi;
        this.conteggioMovimenti++;
    }

    public void prelievo(double _soldi)
    {
        if (this.saldo >= _soldi)
        {
            this.saldo -= _soldi;
            this.conteggioMovimenti++;
        }
    }

    public void stampaInfo()
    {
        System.out.println("|--------------INFO CONTO-----------------|");
        System.out.println("| IBAN: " + this.IBAN);
        System.out.println("| Saldo: " + this.saldo);
        System.out.println("| #Movimenti:" + this.conteggioMovimenti);
        System.out.println("|-----------------------------------------|");
        System.out.println();
    }
}

ContoCorrentePrivato e Giuridico

Le classi figlie non spaventano. In realtà possiedono solo una manciata di attributi in più di cui provvedere a creare i metodi di accesso e creare il costruttore su misura utilizzando il super() con i parametri giusti per richiamare il costruttore vuoto del padre o il costruttore con tutti i parametri. L’altra necessità è specifica in override un nuovo metodo stampaInfo() che ha la stessa firma del padre, ma stampa ovviamente sia gli attributi del padre che quelli aggiuntivi del figlio.

public class ContoCorrentePrivato extends ContoCorrente
{
    private String codiceFiscale;
    private String nome;
    private String cognome;

    public ContoCorrentePrivato(String codiceFiscale, String nome, String cognome)
    {
        super();
        this.codiceFiscale = codiceFiscale;
        this.nome = nome;
        this.cognome = cognome;
    }

    public ContoCorrentePrivato(String IBAN, double saldo, int conteggioMovimenti, String codiceFiscale, String nome, String cognome)
    {
        super(IBAN, saldo, conteggioMovimenti);
        this.codiceFiscale = codiceFiscale;
        this.nome = nome;
        this.cognome = cognome;
    }

    public ContoCorrentePrivato(ContoCorrentePrivato _c)
    {
        super(_c.getIBAN(), _c.getSaldo(), _c.getConteggioMovimenti());
        this.codiceFiscale = _c.getCodiceFiscale();
        this.nome = _c.getNome();
        this.cognome = _c.getCognome();
    }

    // METODI DI ACCESSO
    public String getCodiceFiscale() {
        return codiceFiscale;
    }

    public void setCodiceFiscale(String codiceFiscale) {
        this.codiceFiscale = codiceFiscale;
    }

    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;
    }

    @Override
    public void stampaInfo()
    {
        System.out.println("|--------------INFO CONTO-----------------|");
        System.out.println("| IBAN: " + this.getIBAN());
        System.out.println("| Cognome: " + this.cognome);
        System.out.println("| Nome: " + this.nome);
        System.out.println("| Saldo: " + this.getSaldo());
        System.out.println("| #Movimenti:" + this.getConteggioMovimenti());
        System.out.println("|-----------------------------------------|");
        System.out.println();
    }
}
public class ContoCorrenteGiuridico extends ContoCorrente
{
    private String partitaIVA;
    private String ragioneSociale;

    public ContoCorrenteGiuridico(String partitaIVA, String ragioneSociale)
    {
        this.partitaIVA = partitaIVA;
        this.ragioneSociale = ragioneSociale;
    }

    public ContoCorrenteGiuridico(String IBAN, double saldo, int conteggioMovimenti, String partitaIVA, String ragioneSociale) {
        super(IBAN, saldo, conteggioMovimenti);
        this.partitaIVA = partitaIVA;
        this.ragioneSociale = ragioneSociale;
    }

    public ContoCorrenteGiuridico(ContoCorrenteGiuridico _c) {
        super(_c);
        this.partitaIVA = _c.getPartitaIVA();
        this.ragioneSociale = _c.getRagioneSociale();
    }

    public String getPartitaIVA() {
        return partitaIVA;
    }

    public void setPartitaIVA(String partitaIVA) {
        this.partitaIVA = partitaIVA;
    }

    public String getRagioneSociale() {
        return ragioneSociale;
    }

    public void setRagioneSociale(String ragioneSociale) {
        this.ragioneSociale = ragioneSociale;
    }


    @Override
    public void stampaInfo()
    {
        System.out.println("|--------------INFO CONTO-----------------|");
        System.out.println("| IBAN: " + this.getIBAN());
        System.out.println("| Ragione Sociale: " + this.ragioneSociale);
        System.out.println("| P. IVA: " + this.partitaIVA);
        System.out.println("| Saldo: " + this.getSaldo());
        System.out.println("| #Movimenti:" + this.getConteggioMovimenti());
        System.out.println("|-----------------------------------------|");
        System.out.println();
    }
}

Classe Banca

La classe Banca è la classica classe di contenitore e gestione. In questo caso andiamo a definire una ArrayList che conterrà i vari conticorrente. Il polimorfismo ci consente di creare l’array list col tipo del padre, per poi inserire in modo del tutto arbitrario istanze di ClasseContoCorrentePrivato e ContoCorrenteGiuridico. Occhio: ogni volta che abbiamo una ArrayList va dichiarato come attributo, privato o pubblico che sia, ma nei costruttori va utilizzato il new per inizializzarlo, anche se vuoto.

Cosa pssiamo notare ancora. Senza dubbio le funzioni che ci occorrono per la gestione del nostro arraylist. In questo caso sono inutili o dannose i metodi di accesso all’arraylist. Meglio creare metodi su misura per inserire, cancellare e ricercare gli elementi di tale lista dinamica.

Inserisci conto

Qui abbiamo fatto la scelta di inserire un conto in due modi: o direttamente con un oggetto ContoCorrente (ovvero con il polimorfismo posso passare sia un contocorrente, un contocorrenteprivato ed un contocorrentegiuridico equivalentemente), oppure con una terna di parametri. Il secondo metodo crea al volo un oggetto col costruttore della classe per poi inserirlo nell’arraylist col metodo add().

    public void inserisciConto(ContoCorrente _c)
    {
        this.listaConti.add(_c);
    }

//overloading inserisciConto
    public void inserisciConto(String IBAN, double saldo, int movimenti)
    {
        this.listaConti.add(new ContoCorrente(IBAN, saldo, movimenti));
    }

    public void cancellaConto(String _IBAN)
    {
        for (int i=0; i < this.listaConti.size(); i++)
        {
            if (this.listaConti.get(i).getIBAN() == _IBAN)
            {
                this.listaConti.remove(i);
            }
        }
    }

Cancella conto

La cancellazione di u nconto corrente deve necessariamente passare per una ricerca. Infatto la funzione deve prendere un parametro stringa. La ricerca su un array list non è molto dssimile da quella di un vettore tradizionale con l’unica differenza che si deve prelevare uno o più valoro chiave con metodi di accesso.

    public void cancellaConto(String _IBAN)
    {
        for (int i=0; i < this.listaConti.size(); i++)
        {
            if (this.listaConti.get(i).getIBAN() == _IBAN)
            {
                this.listaConti.remove(i);
            }
        }
    }

Ricerca

Sulla fala riga, i metodi ricerca e modifica. Il primo prende sempre un iban e restituisce un bollean vero o falso se il contocorrente è stato trovato, il secondo restituisce proprio il contocorrente intero se trovato o un valore null. Se ritorno un oggetto, potrò inserirlo in una variabile, eseguirne eventualmente il cast ed usarla con tutti metodi a disposizione.

 public boolean cercaConto(String _IBAN)
    {
        for (int i=0; i < this.listaConti.size(); i++)
        {
            if (this.listaConti.get(i).getIBAN() == _IBAN)
            {
                return true;
            }
        }

        return false;
    }
    
    //@Overloading
    public boolean cercaConto(String _cognome, String _nome)
    {
        for (int i=0; i < this.listaConti.size(); i++)
        {
            if (this.listaConti.get(i) instanceof ContoCorrentePrivato)
            {
                if (new ContoCorrentePrivato ((ContoCorrentePrivato) this.listaConti.get(i)).getNome() == _nome &&
                    new ContoCorrentePrivato ((ContoCorrentePrivato) this.listaConti.get(i)).getCognome() == _cognome)
                {
                    return true;
                }
            }

            if (this.listaConti.get(i) instanceof ContoCorrenteGiuridico)
            {
                if (new ContoCorrenteGiuridico ((ContoCorrenteGiuridico) this.listaConti.get(i)).getPartitaIVA() == _nome &&
                        new ContoCorrenteGiuridico ((ContoCorrenteGiuridico) this.listaConti.get(i)).getRagioneSociale() == _cognome)
                {
                    return true;
                }
            }
        }

        return false;
    }

Totale saldi

Il metodo rimane molto semplice. Scorriamo tutto l’arraylist e creiamo una sommatoria prelevando tutti i saldi dei singoli conti

 public double totaleSaldi()
    {
        double totale = 0;
        for (int i=0; i < this.listaConti.size(); i++)
        {
            totale += this.listaConti.get(i).getSaldo();
        }
        return totale;
    }

    public void stampaInfoConti()
    {
        for (int i=0; i < this.listaConti.size(); i++)
        {
            this.listaConti.get(i).stampaInfo();
        }
    }

Il listato Banca

import java.util.ArrayList;

public class Banca
{
    private String nome;
    private ArrayList <ContoCorrente> listaConti;

    //COSTRUTTORI
    public Banca()
    {
        listaConti = new ArrayList<ContoCorrente>();
    }

    public Banca(String nome)
    {
        this(); //chiama il costruttore vuoto + aggiunge l'inizializzazione di seguito

        this.nome = nome;
    }

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

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

    //METODI ACCESORI
    public void inserisciConto(ContoCorrente _c)
    {
        this.listaConti.add(_c);
    }

//overloading inserisciConto
    public void inserisciConto(String IBAN, double saldo, int movimenti)
    {
        this.listaConti.add(new ContoCorrente(IBAN, saldo, movimenti));
    }

    public void cancellaConto(String _IBAN)
    {
        for (int i=0; i < this.listaConti.size(); i++)
        {
            if (this.listaConti.get(i).getIBAN() == _IBAN)
            {
                this.listaConti.remove(i);
            }
        }
    }

    public ContoCorrente modificaConto(String _IBAN)
    {
        for (int i=0; i < this.listaConti.size(); i++)
        {
            if (this.listaConti.get(i).getIBAN() == _IBAN)
            {
                return this.listaConti.get(i);
            }
        }

        return null;
    }

    public boolean cercaConto(String _IBAN)
    {
        for (int i=0; i < this.listaConti.size(); i++)
        {
            if (this.listaConti.get(i).getIBAN() == _IBAN)
            {
                return true;
            }
        }

        return false;
    }
    
    //@Overloading
    public boolean cercaConto(String _cognome, String _nome)
    {
        for (int i=0; i < this.listaConti.size(); i++)
        {
            if (this.listaConti.get(i) instanceof ContoCorrentePrivato)
            {
                if (new ContoCorrentePrivato ((ContoCorrentePrivato) this.listaConti.get(i)).getNome() == _nome &&
                    new ContoCorrentePrivato ((ContoCorrentePrivato) this.listaConti.get(i)).getCognome() == _cognome)
                {
                    return true;
                }
            }

            if (this.listaConti.get(i) instanceof ContoCorrenteGiuridico)
            {
                if (new ContoCorrenteGiuridico ((ContoCorrenteGiuridico) this.listaConti.get(i)).getPartitaIVA() == _nome &&
                        new ContoCorrenteGiuridico ((ContoCorrenteGiuridico) this.listaConti.get(i)).getRagioneSociale() == _cognome)
                {
                    return true;
                }
            }
        }

        return false;
    }

    public double totaleSaldi()
    {
        double totale = 0;
        for (int i=0; i < this.listaConti.size(); i++)
        {
            totale += this.listaConti.get(i).getSaldo();
        }
        return totale;
    }

    public void stampaInfoConti()
    {
        for (int i=0; i < this.listaConti.size(); i++)
        {
            this.listaConti.get(i).stampaInfo();
        }
    }
}

Classe Main

Non ci resta che creare un main che ci fa da scatola nera e ci permette di testare le classi appena realizzate. Proviamo i costruttori e i vari metodi scritti. Creo una banca, inserisco due conti correnti per testare il polimorfismo: si aspetta un ContoCorrente invece vado ad inserire col new un privato e un giuridico figli. Provo quindi un modificaConto prelevando e versando. La stampainfo deve mostrare quindi poi tutte queste modifiche

public class Main
{
    public static void main(String[] args)
    {
        Banca intesa = new Banca("Intesa San Paolo");
        intesa.inserisciConto(new ContoCorrentePrivato("IT99C1234567890123456789012",
                                                        0,
                                               0,
                                                        "alfredo",
                                                      "centinaro",
                                                    "CNTLRD82A02L103V"));
        intesa.inserisciConto(new ContoCorrenteGiuridico("IT88C1234567890123456789012",
                0,
                0,
                "Alfredo SPA",
                "1234567"));

        intesa.modificaConto("IT99C1234567890123456789012")
                .versamento(1000);
        intesa.modificaConto("IT99C1234567890123456789012")
                .prelievo(200);


        intesa.stampaInfoConti();

        if(intesa.cercaConto("IT99C1234567890123456789012"))
            System.out.println("Trovato IT99C1234567890123456789012");
        else
            System.out.println("Non Trovato IT99C1234567890123456789012");

        System.out.println("Ho creato #"+ContoCorrente.quanticontiHocreato +" conti");
    }
}

Listati completi

GitHub -> https://github.com/alfredocentinaro/esercizi-java/tree/main/gestione-banca

Ultima modifica 6 Aprile 2023