Pour sortir un peu de la théorie, la prochaine étape du bootcamp est un exercice (qu'ils appellent project challenge). J'ai pensé qu'il serait intéressant d'inclure une partie pratique pour consolider le contenu abordé jusqu'à présent.
Ce qui était demandé était de créer un projet qui simule un compte unique dans une banque, contenant le numéro de succursale, le numéro de compte, le nom du client et équilibre. Ces données doivent provenir du terminal et à la fin, un message doit être affiché indiquant que le compte a été créé avec succès et indiquant quelles données ont été saisies. La description originale peut être vue ici.
Eh bien, cela semble assez simple. Mais une chose que j’aime vraiment faire dans toute tâche que je reçois est de la diviser en étapes très courtes, afin d’avoir un chemin clair à suivre. Je fais cela aussi parce que pendant que je dessine le flux dans ma tête, je peux comprendre si cela a du sens avec ce qui a été demandé et je peux rapidement changer le plan d'action si je pense que cela ne fonctionne pas.
Par conséquent, les étapes à suivre sont les suivantes :
Cela ne semble pas être quelque chose de vraiment complexe. En commençant par l'étape 1, nous avons ce qui suit :
TerminalAccount.java
public class TerminalAccount { // eu sei, sou muito criativo private int branch; private String account; private String clientName; private double balance; public void createAccount(){ // aqui a mágica acontece } }
Bien que les modificateurs d'accès n'aient pas été abordés en profondeur jusqu'à présent dans le cours, j'ai choisi de laisser tous les champs privés, imaginant que dans un système bancaire, les données d'agence, de compte, de titulaire et de solde doivent être bien protégées et seule la classe lui-même devrait y avoir accès.
De plus, la méthode createAccount ne doit rien renvoyer, juste afficher un message dans la console, donc son retour est nul.
Cette méthode est le point de départ de certaines des choses qui m'irritent à propos de Java. Au lieu d'avoir une méthode générique qui capture tout type d'informations saisies par l'utilisateur, la classe Scanner, qui est utilisée à cet effet, dispose de méthodes spécifiques pour chaque type primitif. Autrement dit, il y a Scanner.nextLine() pour les Strings, Scanner.nextInt() pour les nombres, Scanner.nextBoolean pour les booléens...
En comparaison, en C#, nous avons une méthode de saisie standard qui renvoie toujours une chaîne, dont le type est ensuite modifié :
String input = Console.ReadLine(); int.TryParse(input, out number) Console.WriteLine(number) //completamente válido
Dois-je écrire un peu plus ? Il a. Mais au moins, je n'ai pas besoin de me souvenir de la syntaxe de toutes les méthodes, d'une seule et de la convertir dans le type que je veux ensuite. Beaucoup plus intuitif.
Mais continuons, il reste encore beaucoup de chemin à parcourir pour résoudre ce problème. L'étape suivante consiste alors à demander à l'utilisateur de saisir les données client pour l'inscription.
TerminalAccount.java
public class TerminalAccount { private int branch; private String account; private String clientName; private double balance; public void createAccount() { Scanner sc = new Scanner(System.in); System.out.print("Por favor, insira o número da agência: "); this.branch = sc.nextInt(); System.out.print("Por favor, insira o número da conta: "); this.account = sc.nextLine(); System.out.print("Por favor, insira o nome do cliente: "); this.clientName = sc.nextLine(); System.out.print("Por favor, insira o saldo inicial: "); this.balance = sc.nextDouble(); System.out.println("Olá " + this.clientName + ", obrigado por criar uma conta em nosso banco. sua agência é " + this.branch + ", conta " + this.account + " e seu saldo " + this.balance + " já está disponível para saque."); } }
Eh bien, apparemment, tout va bien. Instancions cette classe dans notre méthode principale et voyons ce qui se passe.
Main.java
public class Main { public static void main(String[] args) { TerminalAccount account = new TerminalAccount(); account.createAccount(); } }
Bœuf ? Pourquoi la saisie du numéro de compte a-t-elle été ignorée et l'algorithme a-t-il déjà demandé le nom du client ?
En lisant la documentation de la classe Scanner, il est expliqué qu'elle divise l'entrée en jetons en utilisant un type de caractère comme délimiteur, pour savoir où s'arrêter. Dans le cas des méthodes .next() et .hasNext() (et de leurs variantes, telles que .nextInt() et .hasNextInt()), le délimiteur est un espace global (s+) tel que des espaces, des tabulations et des sauts de ligne. .
La méthode prend le jeton précédé du délimiteur, le convertit dans le type spécifié et renvoie cette valeur. Le reste reste dans le tampon d'entrée.
D'accord, jusqu'ici, ok. Le problème est que la méthode .nextLine(), utilisée pour capturer les chaînes consomme le caractère de saut de ligne (n), au lieu de le supprimer. Ensuite, lorsqu'il est utilisé à la suite d'un autre qui le rejette, il lit ce qui a été laissé et termine immédiatement son opération, passant à la ligne de code suivante.
Cela semble déroutant, et ça l'est. J'ai fait un schéma pour vous aider à comprendre la folie qu'est ce flow :
Tá, e como consertamos essa lambança? Simples: adicionando um novo Scanner.nextLine() logo depois do .nextInt() para "limpar" o que sobrou. É bonito? Não, mas resolve.
Dá pra mudar o delimitador para aceitar a quebra de linha (\n ) em vez de um whitespace comum (\s+), mas isso poderia quebrar a forma com que as informações são quebradas em tokens, então é melhor deixar pra lá.
TerminalAccount.java
public class TerminalAccount { private int branch; private String account; private String clientName; private double balance; public void createAccount() { Scanner sc = new Scanner(System.in); System.out.print("Por favor, insira o número da agência: "); this.branch = sc.nextInt(); sc.nextLine(); //... } }
Mais feio que bater na mãe.
Beleza, funcionou. Poderíamos dizer que o exercício está completo, mas vamos por um segundo imaginar que o usuário, sem querer, digitou uma letra na hora de colocar o número da agência:
Eita lasqueira.
Isso acontece porque, como já sabemos, a tipagem do Java é estática e o Scanner está esperando um número mas quando colocamos uma letra junto com um número, essa cadeia se torna uma String. O input que estava esperando um int recebeu uma String e ficou confuso tal qual uma criança que recebe meias de presente de natal.
Para remediar essa situação, podemos criar um loop simples, que informe para o usuário que a informação que ele inseriu está incorreta de acordo com as especificações do sistema e pedir que ele insira os dados novamente (de maneira correta, dessa vez). Como não sabemos quantas tentativas o usuário vai levar para inserir os dados corretamente, um while parece adequado.
public class TerminalAccount { private int branch; private String account; private String clientName; private double balance; public void createAccount() { Scanner sc = new Scanner(System.in); boolean isBranchNumberInputCorrect = false; do { try { System.out.print("Por favor, insira o número da agência: "); this.branch = sc.nextInt(); sc.nextLine(); isBranchNumberInputCorrect = true; } catch (InputMismatchException e) { System.out.println("Por favor, insira apenas números inteiros para o número da agência."); sc.nextLine(); } } while (!isBranchNumberInputCorrect); //... } }
Aqui criamos uma variável de controle chamada IsBranchNumberInputCorrect (porque, novamente, sou muito criativo quando se trata de nomes), inicializada em false. Em seguida, começamos o bloco do, uma vez que queremos que o código faça uma ação antes de verificar se o dado inserido é valido ou não e jogamos nosso input lá pra dentro.
Caso dê tudo certo, o dado inserido será armazenado no campo branch, qualquer caractere sobrando será consumido pelo Scanner.nextLine() e a nossa variável de controle será atualizada para true. Aí a condição do while vai checar se isBranchNumberInputCorrect é false. Se for, reinicia o loop no caso de sucesso, o laço é encerrado.
Agora, caso o usuário insira algo não esperado (como uma String), o método Scanner.nextInt() vai emitir um evento de erro InputMismatchException, que será capturado pelo nosso bloco catch. Uma vez lá dentro, o código vai exibir uma mensagem de erro alertando que o tipo de dado está errado e consumido qualquer caractere que tenha ficado pra trás.
A gente pode fazer a mesma coisa com o input de saldo, para garantir que o valor inserido sempre será numérico e não permitir que a aplicação quebre caso seja inserido algo como 12,56f:
public class TerminalAccount { private int branch; private String account; private String clientName; private double balance; public void createAccount() { Scanner sc = new Scanner(System.in); //... boolean isBalanceInputCorrect = false; do { try { System.out.print("Por favor, insira o saldo inicial: "); this.balance = sc.nextDouble(); sc.nextLine(); isBalanceInputCorrect = true; } catch (InputMismatchException e) { System.out.println("Por favor, insira apenas valores decimais."); sc.nextLine(); } } while (!isBalanceInputCorrect); //... } }
Poderíamos parar por aqui e dar esse exercício como encerrado, mas ainda tem um bug que requer um pouco de atenção. O que aconteceria se, em vez de delimitarmos nosso saldo com uma vírgula (,) usássemos um ponto (por exemplo, 10.56)?
Isso acontece devido ao locale, a adaptação do input à cultura do local. Aqui no Brasil, o decimal é delimitado pela vírgula, então o método não entende que essa separação com ponto é válida.
A documentação da classe Scanner nos mostra que é possível alterar a cultura para uma que atenda ou um ou outro padrão, mas não os dois ao mesmo tempo. Também é possível alterar especificamente o delimitador, para um símbolo ou para outro, mas não os dois ao mesmo tempo.
Um dos métodos para solucionar esse problema não é muito elegante, mas resolve: em vez de capturar o dado diretamente como double, vamos usar o método Scanner.nextLine() para pegar o input como uma String, trocar os pontos por vírgula e tentar trocar o tipo para double.
public class TerminalAccount { private int branch; private String account; private String clientName; private double balance; public void createAccount() { Scanner sc = new Scanner(System.in); //... boolean isBalanceInputCorrect = false; do { try { System.out.print("Por favor, insira o saldo inicial: "); String balanceString = sc.nextLine().replace(",", "."); this.balance = Double.parseDouble(balanceString); isBalanceInputCorrect = true; } catch (NumberFormatException e) { System.out.println("Por favor, insira apenas valores decimais."); } } while (!isBalanceInputCorrect); //... } }
Além da alteração do método de captura do dado, tivemos mais algumas modificações: retiramos as chamadas para o método Scanner.nextLine() que serviam apenas para consumir os caracteres remanescentes, porque nosso input já faz isso pra gente. Além disso, o tipo do erro mudou: agora não se trata de um erro de incompatibilidade de tipo (InputMismatchException), mas sim um de erro no formato do número (NumberFormatException).
Com essas alterações feitas, bora ver se tudo deu certo:
Deu tudo certo! Com isso, conseguimos dizer que o exercício está concluído (finalmente)!
O repositório desse exercício está disponível aqui caso tenha interesse de ver.
E é isso. Até o próximo módulo!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!