<<Previous | ToC
| Next >>
I Googled (something like) "Java input double from keyboard" and got the usual full page of hits on how to do it. Most of them said you do it with the Java Scanner class. Then I Googled "Java Scanner class" and the top hit (Google changes up their search results from time to time, so it might be only near the top when you do it) gave the "docs.oracle.com" website's complete specification of the Scanner class. You should bring it up and read through it at least once, so you get a feel for what you can find on the official documentation page. Notice that they are a little thin on how to use it. That's what StackOverflow and the other Java help/tutorial websites are for. Take some time to read some of them and see if you understand it well enough to write your own code here in your calculator program. Then resume reading here and I'll try to explain the same ideas simpler and more directed to your calculator program.
(Waiting for you to read the other websites...)
In these examples, the Java Scanner class is usually constructed with a file as a parameter -- technically it's a Stream, which also includes anything that pretends to be a file, like a unix pipe -- but for our purposes it's the standard keyboard input System.in. If you looked inside my Zystem class (if you have not already, this might be a good time to do it), you saw that all of my input ("ReadWhatever") functions read characters from that file. Reading from a file might throw exceptions, so I always wrap a "try...catch" around it. Today you will be doing it too.
Anyway, the point of a scanner -- that's a technical word, meaning it scans input text and extracts out tokens by recognizing how they are spelled, for example in Java, if it starts with a digit, it's a number; if a number has a decimal point or the letter 'e' inside, it's a floating point number (double) and so on -- so the scanner's job is to pick out (and usually convert to an appropriate native machine type) the different kinds of things you might want to find in a file. Usually we need this most for converting text input into the numbers that we humans can read just by glancing at it, but in our case today, we want it also to see an operator character, one of the four mathematical operators ( + - * / ) so our calculator knows what to do with the number you give it.
Java library routines try to do as much as possible, so there are a lot of Scanner methods that you will just ignore. First you need to decide what you expect your user to type in. It is the nature of Java functions (methods that return a value) that you need to know in advance what the data type of the value is, so basically you tell it what you want, and if that's not what it sees there, it throws an exception. Making Java programs user-friendly is a lot of work, as you will see.
Most people are familiar with 4-function calculators where you push an operator button, and then you push digits (possibly including a decimal point), and then repeat, or else push '=' to get the final result, or else an intermediate result before continuing. You could do that, but the BlueJ console would make it harder than you'd think. Later, after you have learned the GameEngine, you might try to do it in that environment, where you can get keystrokes as individual events.
For now you might consider requiring your user to type an operator followed by a space followed by a number and a return ("Enter" key), or just an operator and a return (if the operator expects no number, like Clear or Equal). This will work in the Scanner using (A) just two methods, next() to get the operator, and then nextDouble() to get the number. BlueJ will hold off your input until the user types the return, but the scanner will pick off the tokens (one word at a time) ignoring the line ends.
Alternatively, you could (B) use nextLine() to get the whole input line as a String, then pick off your operator using a suitable string method, then pass the rest to another instance of the Scanner class to extract the number from it. Easier than using another instance of Scanner -- perhaps you saw this in your Google search (you did try that, right? You need to, you will be doing this a lot) -- you can (C) use Double.parseDouble(str) to convert the remaining string to double.
Or you could (D) use Zystem.ReadLetter() to read the operator and Zystem.ReadWord() to get the number string, then use Scanner or parseDouble to convert it to double. Or you could (E) take the easy way out and insist that your users can only enter whole numbers, and use Zystem.ReadInt() to read the number as an integer. Oh wait, we already did that.
Let's do them each (more or less) in order...
Scanner scn = new Scanner(System.in);
while (true) {
Scanner does not include a method for reading just a single
non-blank character (type char), so reading it will take two steps.
First we read a (non-blank) word, then we pick out the first character
of that word. I called my String variable aWord:
/// b. accept a command letter:
aWord = scn.next();
optor = aWord.charAt(0);
Then when we are ready to read in the input value,
/// e. accept a value
inval = scn.nextDouble();
You did change your input and result variables (back) to double,
right? Java will complain if you try to put a value of type double
into a variable of type int. Automatic promotion only goes toward
a wider or more general format, never the other way.
What happens when you try to compile this? Besides type faults (if you forgot). Scanner is a standard Java library class, but you must ask for it (like Random, back when we were doing rock-paper-scissors). One of the sites I found when searching Google for how to input double, began their example with the line
import java.util.Scanner; // Import the Scanner classNot all of them did. If you are learning a new class, and none of the help sites bothered to tell you how to spell the import line -- StackOverflow is usually very helpful, but they almost never tell you about the import -- you can look in the documentation page for a line that looks something like this:
java.something.Classnamewhere Classname is whatever class you are working on, and something is the part you don't know: in our case it's ".util." (same as Random) but other classes can have a different package name, or sometimes even multiple names there in the middle. If you are getting a library class from somewhere other than Java, it would have a different name in front too, often the domain name of whoever is making it available.
Now you should be able to compile and run your calculator program. Did
it work OK? Don't forget that the input line now requires a space between
the operator and the value. When I tried messing with the input line, sometimes
the program got very confused, I think the fault of BlueJ (we'll
fix that in the Game Engine), and sometimes it
crashed with an exception. Oh wait, you already have a try-catch inside
your while-loop, right? If you type in random letters instead
of a valid number, you get a new exception "java.util.InputMismatchException"
or something like that. If your program caught it, then it's still running,
you have control, you can do something reasonable. For paid-up users, just
printing out the exception name is not reasonable, they cannot be expected
to know what it means. Instead, your code should attempt to figure out
(inside the catch-clause braces) what went wrong, and what the
user should do about it -- probably in this case, retype their input --
and tell them so politely (beep is not polite, nobody but you knows what
it means).
} // end of tryThen you could know exactly what kind of exception you got (because this is all this particular catch will see), and you can give the user an informed error message. Ideally you would know about all possible exceptions -- they each have a separate class name -- with their own catch clauses, and keep the default to catch any surprises (and fix them before you release the code). The documentation for each class you use should list all the possible exceptions the methods in that class might throw. For example, the Scanner class constructor -- that's the "new Scanner(System.in)" line -- might throw an exception if the input file has a problem. As written, our Calculator program would just crash, because there is no try-catch containing that constructor. You need to watch out for these things when you want to write robust (never-fail) software.
catch (InputMismatchException ex) {
System.out.println("Invalid input, please try again");
} // end of InMmEx catch
catch (Exception ex) {
System.out.println("Unexpected error " + ex.toString());
} // end of default catch
It's probably a good idea if you try each of the other input ideas,
especially if you want to know this like a professional. Otherwise skip
forward to the next page and start learning about
OOPS.
/// e. accept a valueThis will have its own exceptions, which you should check for.
aWord = scn.next();
inval = Double.parseDouble(aWord);
/// b. accept a command letter:
aLine = scn.nextLine();
aWord = aLine.trim(); // removes spaces at the front (and back)
optor = aWord.charAt(0);
here = aLine.indexOf(aWord);
aWord = aLine.substring(here+aWord.length()); // removes operator from aLine
Then when we are ready to read in the input value,
/// e. accept a value
inval = Double.parseDouble(aWord.trim());
All of these string operations have failure modes (they will throw
exceptions) which you need to (a) read up on, and then (b) add remedial
code to deal with them properly. This will run without that remedial code,
but the program will crash or (if you have a generic catch inside
the while) possibly ignore the user input without adequate notification.
As they tell you in school, "That is left as an exercise for the student."
Next: Objects
<<Previous | ToC | Next >>
Revised: 2021 June 2