Extending java.util.Scanner - java

Suppose I would like to add a method to Scanner called nextPositiveInt() which is similar to nextInt() except that when a negative integer is detected, a custom InputNegativeException is thrown.
Why would I want to do this when there are solutions that utilise hasNextInt()? While being a little less concise, it seems a whole lot tidier and more logical given the purpose of exceptions. For example:
Extended Scanner method:
Scanner cli = new Scanner(System.in);
boolean inputValid = false;
do
{
System.out.println("Enter your age:");
try
{
int age = cli.nextPositiveInt();
}
catch(InputNegativeException e)
{
System.out.println("You cannot specify a negative age.");
inputValid = false;
}
catch(InputMismatchException e)
{
System.out.println("Your input must be numeric.");
inputValid = false;
}
} while(!inputValid);
hasNext() method:
Scanner cli = new Scanner(System.in);
do
{
System.out.println("Please enter a positive number!");
while(!sc.hasNextInt())
{
System.out.println("That's not a number!");
sc.next(); // this is important!
}
int number = sc.nextInt();
} while(number <= 0);
So assuming you aren't already firing up a response telling me why this is a very bad idea (if it is, please do; I imagine there might be some objection regarding putting validation in Scanner) I'm confused about how to go about this. I guess I need to replicate the body of nextInt() in nextPositiveInt() with some minor changes? Can you even source the body of nextInt() anywhere?
I apologise that I have no code to show any effort I've made but I'm not sure where to start.

Althought Scanner class is final and you cann't extend it there exist another solution. You can use Delegation pattern.
Also as Scanner class has all nessasary methods public you can easily copy original method and make a little change. See source code of Scanner class the only thing you should change is regexp used for matching string in order to exclude negative ints.
Source code of scanner:
public int nextInt() {
return nextInt(defaultRadix);
}
public int nextInt(int radix) {
// Check cached result
if ((typeCache != null) && (typeCache instanceof Integer)
&& this.radix == radix) {
int val = ((Integer)typeCache).intValue();
useTypeCache();
return val;
}
setRadix(radix);
clearCaches();
// Search for next int
try {
String s = next(integerPattern());
if (matcher.group(SIMPLE_GROUP_INDEX) == null)
s = processIntegerToken(s);
return Integer.parseInt(s, radix);
} catch (NumberFormatException nfe) {
position = matcher.start(); // don't skip bad token
throw new InputMismatchException(nfe.getMessage());
}
}
You should change only:
String s = next(integerPattern());
For you purposes you can hardcode regexp. Original regexp can easily be caught on debug.
Definitely it wouldn't be the best solution in terms of realization - a lot of code to write and many copy-paste, but it would be easy and nice to use.

You cannot extend Scanner since it's final:
public final class Scanner
extends Object
implements Iterator<String>
What I would do is have a helper method in one of my classes:
public static int ensureNonNegative(int val) {
if (val >= 0) {
return val;
} else {
throw new InputNegativeException(val);
}
}
and would use it like so:
int val = ensureNonNegative(scanner.nextInt());

Related

Trying to validate a passed value to a method

I have a program that is trying to validate a passed value. I want a user to input anything and the method that I pass it to will validate whether the input would work.
This is my code:
public static void main(String[]args) {
Scanner input = new Scanner(System.in);
ChequingAccount a = new ChequingAccount();
double deposit = inputCheck("Enter deposit amount: ", input);
a.setDeposit(deposit);
}
public static double inputCheck(String prompt, Scanner input) {
boolean userValid = false;
do {
System.out.print(prompt);
double user;
try {
user = input.nextDouble();
if (user < 0) {
throw new IllegalArgumentException("Value cannot be lower than 0");
}
userValid = true;
} catch (InputMismatchException e) {
System.out.println("The value entered is not a number");
user = inputCheck(prompt, input);
input.nextLine();
} catch (IllegalArgumentException ex) {
System.out.println(ex.getMessage());
user = inputCheck(prompt, input);
}
return user;
} while (!userValid);
}
The code works except for the fact that when the method catches the InputMismatchException, the code then will loop a bunch of times and breaks the program. I thought adding a doWhile loop would solve the issue but it didn't do anything.
you don't need a loop , you need recursion
public static double inputCheck(String prompt, Scanner input) {
double user;
try {
user = input.nextDouble();
if (user < 0) {
throw new IllegalArgumentException("Value cannot be lower than 0");
}
return user;
} catch (InputMismatchException e) {
System.out.println("The value entered is not a number");
return inputCheck(prompt, input);
} catch (IllegalArgumentException ex) {
System.out.println(ex.getMessage());
return inputCheck(prompt, input);
}
}
You're calling your own method from inside; the inputCheck method's code calls inputCheck. This is a somewhat creative way to write a loop.
You also have... a loop.
So you 2 loops, to do the job that one loop should do. That's why all heck breaks loose here. Pick one: Either use the do/while construct (so do not call yourself), or, don't loop, and call yourself. Either one can be made to work here.
The nextLine stuff is irrelevant and not the problem here (in general don't call that; just set the delimiter properly; call scanner.useDelimiter("\\r?\\n") and to get entire lines, use next(), not nextLine().
There are number of approaches that can work for this – iteration, recursion, exception catching, etc. Your solution is mixing several of them together which makes it harder to understand and also harder to fix.
Here is an example that uses a simple while loop, no recursion, no exception catching. It uses hasNextDouble() and, depending on the result, either proceeds to capture the double (by calling nextDouble()), or prints a message (along with consuming and ignoring whatever non-double token is present by calling next()).
public static double inputCheck(String prompt, Scanner input) {
while (true) {
System.out.print(prompt);
if (input.hasNextDouble()) {
double number = input.nextDouble();
if (number < 0) {
System.out.println("Value cannot be lower than 0. Please try again.");
} else {
return number;
}
} else {
System.out.println("The value entered is not a number. Please try again.");
input.next(); // consume and ignore whatever non-double input is waiting on the scanner
}
}
}

Get Java Scanner Input Without Advancing Scanner

Get out the value of the scanner without advancing it - Java
I want to get the value of the input in the scanner without advancing it. Currently, I am using my scanners input as System.in.
final var sc = new Scanner(System.in);
I know of the hasNext methods on scanner, and they are currently my best/only way to check its input without advancing it.
Here is how I ensure a positive integral input from sc for example.
public static int getPositiveIntegerInput(Scanner sc) {
System.out.println("Please input a positive integer");
sc.useDelimiter("\n");
while (!sc.hasNextInt() || sc.hasNext(".*[^\\d].*")) {
System.out.println("Invalid input, please try again");
sc.nextLine();
}
return sc.nextInt();
}
I want to extend this notion of checking sc's input without advancing it to actually getting sc's input without advancing it.
What I have tried to to this point
I have gone through the implementation details of hasNext() on Scanner.
Implementation of hasNext:
public final class Scanner {
public boolean hasNext(Pattern pattern) {
ensureOpen();
if (pattern == null)
throw new NullPointerException();
hasNextPattern = null;
saveState();
modCount++;
while (true) {
if (getCompleteTokenInBuffer(pattern) != null) {
matchValid = true;
cacheResult();
return revertState(true);
}
if (needInput)
readInput();
else
return revertState(false);
}
}
}
It seemed to me at least, that one can get scanner's input from the method getCompleteTokenInBuffer, but truly I don't really understand how it works. I don't know if that method alone gets the value of scanner without advancing it, or if it advances it then something else reverts it back to the state it was in before the input as if it has not advanced at all, or if it gets it in combination with something else, or really how at all.
I have been playing around with invoking the private methods Scanner through Java's reflection API, to try to actually return the token holding sc's input value without actually advancing methods (but to be honest, I'm just playing around with it and don't know how to actually accomplish what I want to do).
public static void main(String[] args) {
final var sc = new Scanner(System.in);
sc.useDelimiter("\n");
var str = "";
try {
Method method = Scanner.class.getDeclaredMethod("getCompleteTokenInBuffer", Pattern.class);
method.setAccessible(true);
str = (String) method.invoke(sc, Pattern.compile(".*"));
} catch (Exception e) {
System.out.println("Well, that didn't work!");
System.out.println("Exception: " + e);
}
System.out.println("getCompleteTokenInBuffer: " + str);
// Prints: "getCompleteTokenInBuffer: null"
}
Note: The method above does not wait for an input before get the value of sc's input and hence returns a value of null.
Goal:
Just to reiterate, I would like to find away to capture and return a Scanner object's input value with actually advancing it.
What you're looking for might otherwise be referred to as a peek function.
This answer on another thread indicates that you might be served by creating a wrapper class around Scanner that implements this functionality, since the Scanner class itself does not implement it.

How to get this piece of code into loop, so that it asks a user input each time until the result is "kill"?

I was practicing this piece of code from the book 'Head First Java' and I'm quite confused on the positioning of the loop here.The code is for creating a kind of game that has a random dotcom word(ex: abc.com) occupying some array elements. here I gave that dotcom word the positions from 3 to 5 in the array, and the user tries guessing the position.
import java.util.Scanner;
public class RunTheGame {
public static void main(String[] args) {
MainGameClass sampleObj= new MainGameClass();
int[] location = {3,4,5};
sampleObj.setdotcomLocationCells(location);
Scanner input= new Scanner(System.in);
System.out.println("Enter your guess");
int userGuess=input.nextInt();
String answer = sampleObj.checkForDotcom(userGuess);
System.out.println(answer);
}
}
package simpleDotComGame;
public class MainGameClass {
int[] DotcomLocationCells;
int numOfHits=0;
public void setdotcomLocationCells(int[] location) {
DotcomLocationCells= location;
}
public String checkForDotcom(int userGuess) {
String result="miss";
for(int cell:DotcomLocationCells) {
if(cell == userGuess) {
result ="hit";
numOfHits++;
break;
}
} // end for loop
if(numOfHits == DotcomLocationCells.length) {
result = "kill";
System.out.println("The number of tries= "+numOfHits);
}
}
do {
<insert code where answer result is created>
} while (!answer.equals("kill"))
upd.: but you must override the equals method for correct use, because if you see how method declared in Object.class you find
public boolean equals(Object obj) {
return (this == obj);
You are allowed to declare the variable before initializing it:
String answer;
do {
answer = sampleObj.checkForDotcom(userGuess);
System.out.println(answer);
} while (!answer.equals("kill");
Also beware of the semantics of Scanner.nextInt(): if it can't parse the input as an int (for example, it contains letters), it will throw an exception, but won't jump over the invalid input. You'll have to use Scanner.nextLine() to force-jump over it, otherwise you'll get an infinite loop.
Rachna, the loop will be positioned, around the following code:
Scanner input= new Scanner(System.in);
System.out.println("Enter your guess");
int userGuess=input.nextInt();
String answer=sampleObj.checkForDotcom(userGuess);
System.out.println(answer);
//For string you must use the equals
if ("kill".equals(answer)){
break;
}
The reason why is that the kill command must be evaluated inside the loop to break it, and the input to continuously ask the user input until he hits all targets.
Here's how the loop should be positioned.
String answer = "";
do{
System.out.println("Enter your guess");
int userGuess=input.nextInt();
answer=sampleObj.checkForDotcom(userGuess);
System.out.println(answer);
}
while(!answer.equals("kill"));
NOTE: Never Check for String equality using == in Java unless you know what you're doing (also read as, unless you know the concept of String Constant Pool).

Putting a check for variable type when using Scanner

I am writing a small program (student, though not an assignment for class...but rather a play on a previous assignment). Previously for class, while learning do/while loops, I wrote a program that prompted a user to input integers. When the user typed in 0, it served to get out of the loop, and then outputted the sum of the squares of all the integers typed.
Example output (double spaced for line breaks):
Type an integer: 3
Type an integer: 0
The sum of the squares is 9
My goal now is to take it a step farther. As written, the program crashes if the user types in anything other than an integer. I have been playing around trying to find ways to allow the user to type in other forms of values, without having it crash. In referencing the code below (which is the program at the moment that does crash at any value sans ints), I tried putting in variations of if statements with the console.hasNextInt() method. Yet my attempts in this would cause an error that number in the do/while test may not have been referenced.
Can anyone offer me any tips? It would be appreciated.
public static void userInterface() {
Scanner console = new Scanner(System.in);
int number;
int numberSquared;
int squaredOutput = 0;
do {
System.out.print("Type an integer (0 to quit): ");
number = console.nextInt();
if (number > 0 || number < 0) {
numberSquared = number * number;
squaredOutput += numberSquared;
}
} while (number != 0);
System.out.println("The sum of the squares is " + squaredOutput);
}
The problem is that you are using console.nextInt(); which only takes the next int.
You can use: console.nextLine();.
It would allow your program to accept a string and you can parse it into an Int when necessary:
try {
number=Integer.parseInt(console.nextLine());
} catch(NumberFormatException e) {
System.out.println("Please input an Integer");
}
Just use this function
public static int next(String message) {
while (true) {
System.out.println(message);
try {
return new Scanner(System.in).nextInt();
} catch (Exception e) {
System.out.println("Invalid input.");
}
}
}
There is problem with your code. When you use console.nextInt() and the scanner try to parse every string as Integer. Better solution is to use console.nextLine() and by your own parse it to your number and catch exception that might be thrown if that string is not parsable as any number that you want.
simply it might look like this.
String yourValue = console.nextLine();
try{
int value = Integer.parseInt(yourValue);
}catch(NumberFormatException e){
System.out.println("watch out this value is unparsable!");
}

Input validation without using try catch exception

Im writing a program that calculates the investment of a person after a number of years. I prompt the users to enter their name, amount they will be investing, interest rate, and number of years. I'm supposed to do a validation of the input with if...else statements. One of the checks is to see if the user has entered the correct data type. This is for an intro java class. We finished the chapter on methods a week ago, so this is beginner's stuff. I can seem to figure out how to do the data type check. I tried the hasNextInt for my int types but I get an exception which we haven't learned at all. I found some info online on the Pattern and Match classes but there's a lot of stuff in there that we haven't seen yet. Here's one of the methods I wrote to get the correct input.
//Define method for input validation, integer type
public static int getValidInt(String messagePrompt, String messagePrompt2, String messagePrompt3){
Scanner input = new Scanner(System.in);//Create scanner
int returnValue;
int j = 0;
do {//Start validation loop
System.out.printf(messagePrompt); //Print input request
returnValue = input.nextInt();
if (returnValue <= 0) { //Check if user entered a positive number
System.out.println(messagePrompt2);//Print error message
}
else if (!input.hasNextInt()) {
System.out.println(messagePrompt3);//Print error message
}
else {
j++;
}
} while (j == 0);//End validation loop
return returnValue;
}
Im not sure if I have the order of the checks right. Any help is welcome. Thank you.
If it's just 4 pre-defined input fields and you don't have to check for additional things then I don't see a reason to use a do while loop here. Though maybe I don't get what this method is supposed to do, are you returning some kind of integer that defines whether the input was valid or do you actually have to store the values? If the former, why not just return a Boolean or an Enumeration?
I also don't understand why you're simply calling nextInt the first time, but for the next one you are checking whether it has a nextInt.
Also you don't mention what kind of exception you're getting when calling hasNextInt, but apparently this can only be an IllegalStateException. I suggest taking a look at the Java docs at http://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html, or reading your relevant course material.
The sequence nextInt() and hasNextInt() is invoked. First one is used to read the value from input, and second is used to see whether the value type is int. So you have to invoke hasNext[Type]() followed by next[Type].
Let's correct those two first as below.
if (scnr.hasNextInt()) {
int userChoice = scnr.nextInt();
} else {
// input is not an int
}
Now let's correct your code to get a valid int.
public static int getValidInt(String messagePrompt, String messagePrompt2, String messagePrompt3) {
Scanner input = new Scanner(System.in);// Create scanner
int returnValue = -1;
boolean incorrectInput = true;
while (incorrectInput) {
System.out.printf(messagePrompt); // Print input request
if (input.hasNextInt()) {
returnValue = input.nextInt();
if (returnValue <= 0) { // Check if user entered a positive number
System.out.println(messagePrompt2);// Print error message
} else {
incorrectInput = false;
}
} else {
input.next();
System.out.println(messagePrompt3);// Print error message
}
}
return returnValue;
}

Categories

Resources