Olá! Sou iniciante em java e estou com um problema.
Estou criando um gerador de senha aleatória e em uma das etapas o usuário entra com o tamanho da senha que deseja gerar. Criei o método a seguir para fazer a leitura, porém se ele digita uma letra, por exemplo, o programa dá errou pois estou tentando ler um inteiro e não foi esse o tipo lido.
private static int readLength() {
Scanner scanner = new Scanner(System.in);
return scanner.nextInt();
}
Eu pensei em fazer a leitura de uma string ao invés de inteiro, que aí abrangeria qualquer coisa que o usuário digitar, e trabalhar na verificação da string antes de continuar. Se for uma boa ideia, como recomendam essa verificação? E se não for, o que acham que seria prático fazer? Não me parece ser um problema para ter que criar um novo método ou classe só pra tratar isso, mas como sou iniciante, queria essa opinião de vocês.
O problema de chamar o método dentro dele mesmo é que se isso se repetir um número suficiente de vezes, pode causar um estouro de pilha - pois cada chamada recursiva é empilhada e elas ficam ocupando espaço na pilha até que uma delas retorne (no caso, somente quando um número válido for digitado). Veja aqui como ocorre o StackOverflowError depois de várias chamadas (fiz um exemplo sem Scanner apenas para mostrar como o problema ocorre, então imagine que o usuário digitou errado muitas vezes, o erro seria o mesmo).
Claro que se rodar poucas vezes, o problema não ocorre, mas nesse caso, por que não usar simplesmente um loop, que evita esse problema em qualquer situação?
private static int readLength() {
while (true) {
try {
System.out.print("Insira um número: ");
return Integer.parseInt(scan.nextLine());
} catch (final NumberFormatException e) {
System.out.println("Número inválido!");
}
}
}
Enquanto não for digitado um número, o while continua. Quando for digitado um número, o método retorna. E nunca ocorrerá o estouro de pilha, pois é somente uma chamada do método (em vez de uma nova chamada a cada vez que digitar errado).
Não use recursão onde não precisa (e na maioria das vezes não precisa).
Outro detalhe é que não precisa fechar o System.in. Claro que fechar qualquer recurso que abrimos geralmente é uma boa ideia, mas o System.in é “especial” (gerenciado pela JVM) e na prática não precisa fechá-lo (leia mais sobre isso aqui e aqui).
Porque eu não tinha pensado nisso. Sua idéia ficou muito melhor!
Entendi. Eu sempre recomendo pra quem pede ajuda aqui que feche o Scanner, inclusive usando o try-with-resources. Eu achava que só fechava o Scanner e não o System.in.
No caso o melhor a se fazer é ignorar os avisos e nunca fechar? Sei que pros exercicios que vemos aqui não vai fazer diferença e pra um projeto sério que realmente precise do Scanner precisaria de uma solução mais robusta.
Ao fechar o Scanner, a stream que ele usa também é fechada.
Claro que, como regra geral, sempre é bom fechar todo recurso que abrimos, mas o System.in é exceção, porque ele é gerenciado pela JVM e uma vez fechado, você não consegue reabri-lo (o mesmo vale para System.out e System.err, aliás).
Essa foi a linha que resolveu meu problema, já fazendo a verificação na própria leitura, muito bom!
Sim, o problema do StackOverFlow já havia me ocorrido em exercícios passados e já estava usando este recurso do loop em outra parte do código, onde o usuário entrava com um SIM ou NÃO para “incluir letras maiúsculas?” “incluir números?” “incluir caracteres especiais?” e como a resposta permite somente duas opções, coloquei assim:
private static boolean readOption() {
Scanner scanner = new Scanner(System.in);
String option;
do {
option = scanner.nextLine();
if (option.compareToIgnoreCase("s") == 0) {
return true;
} else if (option.compareToIgnoreCase("n") == 0) {
return false;
} else {
System.out.print("Opção inválida, digite novamente (s/n) ");
}
} while (true);
}
Não sei se é o mais prático, mas ficou funcional
Foi mais na intenção de ao invés de retornar direto o scan.nextLine(), salvaria em uma variável aux e tentaria algo do tipo aux intanceof int para a verificação antes de dar continuidade, mas o Integer.parseInt(scan.nextLine()) foi cirúrgico.