Neofita: un vostro commento sulle mie prime righe di codice

di il
12 risposte

Neofita: un vostro commento sulle mie prime righe di codice

Buongiorno a tutti!
Ho da pochissimo iniziato ad interessarmi di programmazione, più che altro per sfizio personale. Tra ieri e oggi mi sono informato un po' su Java e su come scrivere qualche semplicissimo programmino.
Ho scritto questa calcolatrice davvero semplice, per fare pratica, mi piacerebbe sapere da voi cosa posso migliorare e semplificare. Tenete conto che moltissimi metodi saranno certamente più complessi del necessario, d'altronde dovevo fare un po' di pratica.
Il programma si compone di due classi: calc e calcola (nomi totalmente casuali!). Calc è il programma principale, calcola contiene i constructor che definiscono gli oggetti somma, moltiplica, dividi e sottrai (ripeto: dovevo fare pratica).

Ecco qui il codice della classe main:
import java.util.Scanner;

class calc
{
	static int Num1, Num2, Result;
	static String altro;
	static String operazione;
	static Scanner calc = new Scanner(System.in);
    static Scanner again = new Scanner(System.in);
    static Scanner oper = new Scanner(System.in);
    static boolean validate;
	public static void main (String[] args)
	{
		calcola EseguiOperazione;
		EseguiOperazione = new calcola();
    	do
		{do
		{
			System.out.println("Inserisci il primo numero: ");
			if(!calc.hasNextInt()) {
				validate = false;
				System.out.println("Inserisci un numero!");
				System.exit(0);
			}else {
				validate = true;
			}
		}while (validate = false);
		Num1 = calc.nextInt();
		do {
			System.out.println("Inserisci il secondo numero: ");
			if(!calc.hasNextInt()) {
				validate = false;
				System.out.println("Inserisci un numero!");
				System.exit(0);
			}else {
				validate = true;
			}
		} while (validate = false);
		Num2 = calc.nextInt();
		do
			{
			System.out.println("Inserisci il tipo di operazione");
			System.out.println("A. Addizione");
			System.out.println("B. Sottrazione");
			System.out.println("C. Moltiplicazione");
			System.out.println("D. Divisione");
			operazione = oper.nextLine();
			switch (operazione.toUpperCase())
			{
			case "A": 
				Result = EseguiOperazione.somma(Num1, Num2);
				break;
			case "B": 
				Result = EseguiOperazione.sottrazione(Num1, Num2);
				break;
			case "C": 
				Result = EseguiOperazione.moltiplicazione(Num1, Num2);
				break;
			case "D": 
				Result = EseguiOperazione.divisione(Num1, Num2);
				break;
			default: System.out.println("Immetti un valore corretto.");
			System.out.println(operazione);
			}
			} while(!(operazione.equalsIgnoreCase("A") || operazione.equalsIgnoreCase("B") || operazione.equalsIgnoreCase("C") || operazione.equalsIgnoreCase("D")));
		System.out.println("Il risultato è: ");
		System.out.println(Result);
		do 
			{
			System.out.println("Altro calcolo? ");
			altro = again.nextLine();
			} while(!(altro.toLowerCase().startsWith("s") || altro.toLowerCase().startsWith("n")));
		}
		while(altro.toLowerCase().startsWith("s"));
	}
}
Nello specifico mi piacerebbe, oltre a consigli generali, sapere come evitare che all'inserimento dei valori il programma crashi se si inserisce erroneamente un valore non-integer. Ho fatto in modo che il programma sputi fuori un errore ed esca, ma non mi riesce la parte in cui propone nuovamente di inserire il valore...

Grazie a tutti in anticipo!

12 Risposte

  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    camazza ha scritto:


    mi piacerebbe sapere da voi cosa posso migliorare e semplificare.
    A prima vista, sicuramente, sono migliorabili: stile di scrittura, spaziature/indentazioni, denominazioni.

    Come seconda cosa: quando si usa Scanner agganciato a System.in va usato un SOLO Scanner e creato UNA volta sola. Non ne servono di più.

    camazza ha scritto:


    Nello specifico mi piacerebbe, oltre a consigli generali, sapere come evitare che all'inserimento dei valori il programma crashi se si inserisce erroneamente un valore non-integer.
    Innanzitutto hai messo dei System.exit(0); che lì non mi sembrano certo il massimo ... Comunque se l'utente inserisce un non-numero es. d45g, hasNextInt() restituisce false ma attenzione il token NON viene buttato via da Scanner (funziona così, non è un "baco"). Generalmente la cosa più semplice da fare è "buttare" via il token con un next() fittizio, senza usare il valore di ritorno (che sarebbe quel token).

    Inoltre: è vero che hai solo 2 input di un numero ma prova a pensare ad un metodo a parte che riceve un testo di "prompt", chiede il numero in input e se non valido lo richiede (riproponendo il prompt) finché non è valido. E quando valido lo restituisce.

    P.S. Se ti interessano esercizi, vedi i miei esercizi in firma. Finora ne ho sviluppati solo 5 ma ne farò molti altri nelle prossime settimane.
  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    Grazie, sei stato incredibilmente gentile!

    Quindi dici che mi converrebbe convalidare l'input in un metodo definito su una classe separata dal programma principale? Devo ancora chiarire un po' di cose sulla programmazione ad oggetti, ovvero quando fare le cose esternamente o quando conviene tenerle "dentro"...
    In ogni caso mi metterò al lavoro subito, credo di aver capito un po' di cose che si possono migliorare.

    Un'ultima cosa, sprando di non approfittare della tua gentilezza: ho eseguito i calcoli in una classe a parte, definendo l'oggetto calc dalla classe calcolo. Volevo chiaramente allenarmi ma mi sembra un approccio poco "pulito" dover creare la classe "calcola" nella quale definire i vari metodi (le operazioni) poi creare un oggetto per poterli richiamare... C'è un metodo più semplice? Ovvero definire un metodo è richiamarlo direttamente senza passare per la fase di "creazione" con new?

    Lo so, son proprio un cane ma la mia curiosità mi dice di approfondire!
  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    camazza ha scritto:


    Quindi dici che mi converrebbe convalidare l'input in un metodo definito su una classe separata dal programma principale?
    Prima intendevo un metodo anche dentro la tua classe principale. Ma se fai una classe a parte es. StdIO dedicata solo all'input/output "standard" (input da System.in e output su System.out) .. sarebbe ancora meglio!
    Qualcosa da usare tipo es.:
    StdIO sio = new StdIO();
    int v = sio.readInt("Inserisci un intero: ");

    camazza ha scritto:


    Devo ancora chiarire un po' di cose sulla programmazione ad oggetti, ovvero quando fare le cose esternamente o quando conviene tenerle "dentro"...
    Sono questioni di "design" delle classi, che dovrai apprendere man mano ...

    camazza ha scritto:


    ho eseguito i calcoli in una classe a parte, definendo l'oggetto calc dalla classe calcolo. Volevo chiaramente allenarmi ma mi sembra un approccio poco "pulito" dover creare la classe "calcola" nella quale definire i vari metodi (le operazioni) poi creare un oggetto per poterli richiamare... C'è un metodo più semplice?
    A dire il vero, il fatto di aver messo i soli metodi delle operazioni in una classe separata (la "calcola") e poi istanziarla con new ... di per sé non è così sbagliato come pensi. Insomma è anche accettabile.
    Non hai mostrato il codice della calcola, presumo non abbia "stato" (variabili di istanza). Se è così, in effetti, si potrebbe obiettare che creare una istanza (o due o tre), non farebbe differenza, visto che non mantiene alcun stato. E quindi non sarebbe particolarmente utile, pertanto si potrebbero mettere quei metodi come static ("di classe") e quindi invocarli es. Calcola.somma( .... );

    (segui le convenzioni standard sui nomi: NomeDiClasse nomeDiVariabile nomeDiMetodo NOME_DI_COSTANTE nota i maiuscolo/minuscolo)
  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    Grazie ancora dei preziosissimi consigli. Giusto per informazione, questo è il codice della classe "calcola"
    
    public class calcola {
    int somma (int a, int b) {
    return a + b;
    }
    int sottrazione (int a, int b) {
    	return a - b;
    }
    int moltiplicazione (int a, int b){
    	return a * b;
    }
    int divisione (int a, int b){
    return a / b;
    }
    }
    
    Ho provato ad integrare calc.Next() per svuotare il token (sperando sia corretto così), rimuovendo il System.exit(0) che avevo inserito solo per evitare il crash con errore di Mismatch. Avrei potuto inserire un try e catch ma volevo un risultato più pulito.
    Tuttavia se inserisco una stringa al posto di un Int il programma non crasha (e fin qui ci siamo) ma avanza comunque alla richiesta del secondo numero! Sarà perché son troppo assonnato, ma non riesco a venirne a capo! Incollo la porzione di codice:
    do
    		{
    			System.out.println("Inserisci il primo numero: ");
    			if(!calc.hasNextInt()) {
    				validate = false;
    				System.out.println("Inserisci un numero!");
    				calc.next();
    			}else {
    				validate = true;
    				Num1 = calc.nextInt();
    			}
    		}while (validate = false);
    		do {
    			System.out.println("Inserisci il secondo numero: ");
    			if(!calc.hasNextInt()) {
    				validate = false;
    				System.out.println("Inserisci un numero!");
    				calc.next();
    			}else {
    				validate = true;
    				Num2 = calc.nextInt();
    			}
    		} while (validate = false);
    ...e questo è ciò che accade
    Inserisci il primo numero: 
    test
    Inserisci un numero!
    Inserisci il secondo numero: 
    Un po' alla volta ce la farò...
  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    camazza ha scritto:


    questo è il codice della classe "calcola"
    E infatti, come pensavo, non ha variabili di istanza. Ovvero, un oggetto calcola non mantiene uno "stato". Ma, ti ripeto, questo è il problema minore, la classe calcola di per sé può anche andare bene così.

    camazza ha scritto:


    Ho provato ad integrare calc.Next() per svuotare il token (sperando sia corretto così), rimuovendo il System.exit(0) che avevo inserito solo per evitare il crash con errore di Mismatch. Avrei potuto inserire un try e catch ma volevo un risultato più pulito.
    Tuttavia se inserisco una stringa al posto di un Int il programma non crasha (e fin qui ci siamo) ma avanza comunque alla richiesta del secondo numero! Sarà perché son troppo assonnato, ma non riesco a venirne a capo!
    Ti faccio presente un'altra cosa. L'input da standard-input di norma è "bufferizzato", ovvero quello che scrivi su standard-input arriva alla applicazione soltanto quando si preme invio. Quindi tu puoi scrivere quanti token vuoi separati da spazi (Scanner usa una sequenza di whitespace come separatore predefinito) ma essi arrivano alla applicazione solo quando premi invio.

    Per questo motivo, una cosa buona da fare è quella di usare sempre nextLine() che prende una linea intera e poi usare i parseInt, parseLong ecc.. di Integer, Long ecc... per convertire se serve altro che non sia una stringa.
    Prova a pensare questo utilizzo. Se poi proprio non riesci, ti faccio un esempio basilare.
  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    Ok, ho fatto un po' di modifiche. Mi sfugge ancora qualcosa dato che devo inserire 3 o 4 righe di input perché il programma proceda.
    Vediamo dove ho sbagliato... questo è il codice che corrisponde all'input della classe main:
    do
    		{
    			System.out.println("Inserisci il primo numero: ");
    			while(CheckIO.inputValido(userinput.nextLine()) == false) {
    			System.out.println("Inserisci un numero!");
    			userinput.nextLine();
    			}
    			Num1 = userinput.nextInt();
    		}while (!userinput.hasNextInt());
    L'input è verificato dal metodo inputValido nella classe CheckIO in questo modo:
    public class CheckIO {
    public static boolean inputValido(String input) {
    	boolean valido = true;
    try{
    	Integer.parseInt(calc.userinput.nextLine());
    }catch(NumberFormatException e){
    		valido = false; }
    	return valido;
    }
    }
    Non ne riesco proprio a venire a capo! Forse ogni volta che viene chiamato lo scanner NextLine lui attende ulteriore input e quindi alla fine questi si sommano?
  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    Quest'ultima cosa che hai scritto è molto ingarbugliata ... usi un mix di nextLine, parseInt, nextInt, hasNextInt che non serve.
    import java.util.Scanner;
    
    public class StdIO {
        private Scanner scanner;
    
        public StdIO() {
            scanner = new Scanner(System.in);
        }
    
        public int readInt(String prompt) {
            while (true) {
                System.out.print(prompt);
                String line = scanner.nextLine();
    
                try {
                    return Integer.parseInt(line);
                } catch (NumberFormatException e) {
                    System.out.println("Attenzione: valore non numerico!");
                }
            }
        }
    }
    Da usare esattamente come dicevo prima.
  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    andbin ha scritto:


    import java.util.Scanner;
    
    public class StdIO {
        private Scanner scanner;
    
        public StdIO() {
            scanner = new Scanner(System.in);
        }
    
        public int readInt(String prompt) {
            while (true) {
                System.out.print(prompt);
                String line = scanner.nextLine();
    
                try {
                    return Integer.parseInt(line);
                } catch (NumberFormatException e) {
                    System.out.println("Attenzione: valore non numerico!");
                }
            }
        }
    }
    Ti ringrazio, mi hai chiarito le idee! Se non ho capito male questo codice crea un constructor, nel quale crei un nuovo oggetto Scanner.
    Tuttavia non mi è chiaro perché il metodo readInt viene inizializzato come variabile (visto che è preceduto da int). Inoltre while (true) non capisco a cosa si riferisca visto che non ci sono boolean.
    Il resto (try ... catch) mi è invece chiaro. Perdonami ma faccio il possibile
  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    camazza ha scritto:


    non mi è chiaro perché il metodo readInt viene inizializzato come variabile (visto che è preceduto da int).
    Non c'entra niente con le variabili. readInt è un metodo e quel int è la dichiarazione del tipo restituito. I metodi in generale hanno la forma

    modificatori tiporestituito nomeMetodo( ..eventuali parametri.. )

    camazza ha scritto:


    Inoltre while (true) non capisco a cosa si riferisca visto che non ci sono boolean.
    true È un boolean (è un valore "letterale" predefinito) e vuol dire "vero". In teoria quindi il while con una condizione sempre "vera" di per sé sarebbe un ciclo infinito che non termina mai.
    In realta può terminare .. c'è un return, se noti. Quindi l'unico motivo per uscire dal metodo è che parseInt non lanci la eccezione e restituisca quindi il int che è ovviamente a quel punto parsato correttamente.
  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    Dunque... GIURO che sto imparando qualcosa ma continuo a bloccarmi

    Ho implementato il codice da te molto gentilmente suggerito. Nel mio main ho dunque richiamato il metodo readInt passandogli la stringa prompt che corrisponde alla richiesta che voglio fare (Inserisci il primo numero).
    String request;
        		Num1 = (CheckIO.readInt(request = "Inserisci il primo numero: "));
    Il programma però crasha (prima di inserire alcunché) con:
    Exception in thread "main" java.lang.NullPointerException
    	at CheckIO.readInt(CheckIO.java:13)
    	at calc.main(calc.java:17)
    
    Dove potrebbe risiedere il problema? Tieni conto che del tuo codice ho modificato il nome della classe (affinché corrispondesse al nome del file già presente) e ho reso statico il metodo readInt (altrimenti non me lo faceva richiamare in main).
  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    La classe che ho mostrato io è da usare come avevo indicato in precedenza:
    StdIO sio = new StdIO();
    int v = sio.readInt("Inserisci un intero: ");
    Presumo non hai istanziato lo Scanner ...
  • Re: Neofita: un vostro commento sulle mie prime righe di codice

    Perfetto! Ora funziona tutto correttamente
    CheckIO valida = new CheckIO();
           		Num1 = (valida.readInt("Inserisci il primo numero: "));
    		Num2 = (valida.readInt("Inserisci il secondo numero: "));
    Ti ringrazio infinitamente della disponibilità. Adesso diverse cose mi sono nettamente più chiare
Devi accedere o registrarti per scrivere nel forum
12 risposte