How about a readln for numbers
Brian Goetz
brian.goetz at oracle.com
Thu Nov 7 19:41:48 UTC 2024
Short answer: Absolutely not.
More discussion elsewhere in this thread.
On 11/7/2024 2:16 PM, Kenneth Fogel wrote:
>
> The readln method introduced as part java.base is great because it
> includes the prompt for the input. The only shortcoming is that if the
> input must be a number then you still need to employ a static class
> member such as Double.parseDouble() to make it a number:
>
> // Currently
>
> var loan = Double.parseDouble(readln("Loan: "));
>
> // My delusional idea
>
> var loan = readDbl("Loan: ");
>
> I can think of reasons why this could be a bad idea. I taught my
> students to use a construct using Scanner to create a console input
> that passed my Beethoven test (while humming Beethoven’s 5^th symphony
> randomly strike keys on your keyboard as if you were playing the
> symphony and your code should just report invalid input and not an
> exception). If we had a readInt and/or readDbl they would not pass
> this test. That Python would happily accept anything and then fail
> when the value was used in a calculation is no better. But, for
> learning Java could we have a readInt or readDbl alongside readln? I
> know that DataInputStream has such methods, but without a prompt and
> it must be attached to an input stream such as a file.
>
> As always, just thinking out loud. Feel free to use my name in vain.
>
> Ken
>
> This message and a back and forth with Ethan McCue began in the
> OpenJDK Discus list and we were asked to move it here. Here is the
> summary of messages in the order they were sent to the list. The
> message above is the last one.
>
> void main() {
> int number;
> do {
> String input = readln("Enter a number between 1 and 10: ");
> try {
> number = Integer.parseInt(input);
> if (number < 1 || number > 10) {
> println("Number out of range");
> }
>
> // All is well
> break;
> } catch (NumberFormatException _) {
> println("You have not entered a number");
> }
> } while (true);
>
> println(number);
> }
>
> For color on how that might affect the pedagogy:
>
> "First we need to get input from the user, this is a String which
> represents text"
>
> void main() {
> String input = readln("Enter a number between 1 and 10: ");
> }
>
> "We can turn a String into an int by writing Integer.parseInt(<text
> here>). If what they wrote isn't a number it will crash"
>
> void main() {
> String input = readln("Enter a number between 1 and 10: ");
> int number = Integer.parseInt(input);
> }
>
> "To handle crashes we can put the code that might crash inside of try
> { }, then write the way it might crash in a catch clause"
>
> void main() {
> String input = readln("Enter a number between 1 and 10: ");
> try {
> int number = Integer.parseInt(input);
> println("You gave: " + number);
> } catch (NumberFormatException e) {
> println("Bad number: " + input);
> }
> }
>
> "If they gave a number that wasn't between 1 and 10 ..."
>
> void main() {
> String input = readln("Enter a number between 1 and 10: ");
> try {
> int number = Integer.parseInt(input);
> if (number < 1 || number > 10) {
> println("Number is not between 1 and 10: " + number);
> }
> else {
> println("You gave: " + number);
> }
> } catch (NumberFormatException e) {
> println("Bad number: " + input);
> }
> }
>
> "Now we want to keep asking them until they give a valid input. To do
> this, first delay the assignment of number"
>
> void main() {
> String input = readln("Enter a number between 1 and 10: ");
> int number;
> try {
> number = Integer.parseInt(input);
> if (number < 1 || number > 10) {
> println("Number is not between 1 and 10: " + number);
> }
> else {
> println("You gave: " + number);
> }
> } catch (NumberFormatException e) {
> println("Bad number: " + input);
> }
> }
>
> ... and so on until you reach
>
>
> void main() {
> String input = readln("Enter a number between 1 and 10: ");
>
> int number;
> do {
> try {
> number = Integer.parseInt(input);
> if (number < 1 || number > 10) {
> println("Number is not between 1 and 10: " + number);
> }
> else {
> break;
> }
> } catch (NumberFormatException e) {
> println("Bad number: " + input);
> }
> } while (true);
>
> println("You gave: " + number);
> }
>
> On Thu, Nov 7, 2024 at 12:50 PM Ethan McCue <ethan at mccue.dev> wrote:
>
> So with all that context, do you see why accepting
> Double.parseDouble(readln()); might be worth it?
>
> "Paving the onramp" shouldn't mean only "paving the first 48 hours."
> The convenience afforded by a dedicated readDouble, at least to me,
> feels outweighed
>
> by
>
> * The loss of a perfectly good opportunity to explain the fundamentals
> of parsing (strings represent text, you can interpret that text by....)
> * The loss of a perfectly good opportunity to teach basic exception
> handling
> * The divergence with the behavior of Scanner
> * The combinatorial explosion of "why not readByte?"
>
> * The privileged position it puts primitives in as the end-point of
> interpreting user input
> * Guns, Feet
>
> (I've separately voiced my concerns about readln in this regard.)
>
> On Thu, Nov 7, 2024 at 12:40 PM Kenneth Fogel
> <kfogel at dawsoncollege.qc.ca> wrote:
>
> Here is an example of a Beethoven test passing routine that expects a
> number for 1 to 10. The Scanner, sc, has already been initialized. I
> also taught how to use regular expressions to fine tune what is
> acceptable input and the acceptable range such that you only need to
> use readLine so that subsequent input is not messed up when a user
> enters 23 45 instead of 23.45. Notice that this routine cleans out the
> buffer with a nextLine at the end.
>
> int number;
>
> do {
>
> System.out.println("Enter a number between 1 and 10: ");
>
> if (sc.hasNextInt()) { // Check that there is an integer in the
> keyboard buffer
>
> number = sc.nextInt(); //
>
> // Check if the number is in range
>
> if (number < 1 || number > 10) {
>
> System.out.println("Number out of range.");
>
> }
>
> } else { // There was not an integer in the keyboard buffer
>
> number = -1; // a value that will keep execution in the loop
>
> System.out.println("You have not entered a number");
>
> }
>
> sc.nextLine(); // Clean out the buffer
>
> } while (number < 1 || number > 10);
>
> This tells me that in the paving the onramp universe a readInt or
> readDbl must clear the keyboard buffer when it encounters a
> terminating character such as the space, tab, and \n. Already I can
> hear the roar over how this breaks the expected behaviour of Scanner
> such as allowing a list of primitives in the keyboard buffer.
>
> Ken
>
> *From:*Ethan McCue <ethan at mccue.dev>
> *Sent:* November 7, 2024 11:43 AM
> *To:* Kenneth Fogel <kfogel at dawsoncollege.qc.ca>
> *Cc:* discuss at openjdk.org
> *Subject:* Re: How about a readln for numbers?
>
> Currently, "don't mix nextLine with next/next int/etc" is an extremely
> common footgun.
>
> In one of the coding help discords, this is the auto message we send
> when people run into trouble with that.
>
> Mixing any |nextXXX|method with |nextLine|from the |Scanner|class for
> user input, will not ask you for input again but instead result in an
> empty line read by |nextLine|. To prevent this, when reading user
> input, always only use |nextLine|. If you need an |int|, do
>
> int||value||=|Integer.parseInt(scanner.nextLine());|
>
> instead of using |nextInt|. Assume the following:
>
> Scanner||scanner||=||new||Scanner|(System.in);|
> ||
> |System.out.println(|"Enter your age:"|);|
> int||age||=|scanner.nextInt();|
> |System.out.println(|"Enter your name:"|);|
> String||name||=|scanner.nextLine();|
> ||
> |System.out.println(|"Hello "|+ name + |", you are "|+ age + |" years old"|);|
>
> When executing this code, you will be asked to enter an age, suppose
> you enter |20|. However, the code will not ask you to actually input a
> name and the output will be:
>
> |Hello , you are |20|years old.|
>
> The reason why is that when you hit the |enter|button, your actual
> input is
>
> 20|\n|
>
> and not just |20|. A call to |nextInt|will now consume the |20|and
> leave the newline symbol |\n|in the internal input buffer of
> |System.in|. The call to |nextLine|will now not lead to a new input,
> since there is still unread input left in |System.in|. So it will read
> the |\n|, leading to an empty input. So every user input is not only a
> number, but a *full line*. As such, it makes much more sense to also
> use |nextLine()|, even if reading just an age. The corrected code
> which works as intended is:
>
> Scanner||scanner||=||new||Scanner|(System.in);|
> ||
> |System.out.println(|"Enter your age:"|);|
> // Now nextLine, not nextInt anymore
> int||age||=|Integer.parseInt(scanner.nextLine());|
> |System.out.println(|"Enter your name:"|);|
> String||name||=|scanner.nextLine();|
> ||
> |System.out.println(|"Hello "|+ name + |", you are "|+ age + |" years old"|);|
>
> The |nextXXX|methods, such as |nextInt|can be useful when reading
> multi-input from a single line. For example when you enter |20 John|in
> a single line.
>
> On Thu, Nov 7, 2024, 11:36 AM Kenneth Fogel
> <kfogel at dawsoncollege.qc.ca> wrote:
>
> The readln method introduced as part java.base is great because it
> includes the prompt for the input. The only shortcoming is that if the
> input must be a number then you still need to employ a static class
> member such as Double.parseDouble() to make it a number:
>
> // Currently
>
> var loan = Double.parseDouble(readln("Loan: "));
>
> // My delusional idea
>
> var loan = readDbl("Loan: ");
>
> I can think of reasons why this could be a bad idea. I taught my
> students to use a construct using Scanner to create a console input
> that passed my Beethoven test (while humming Beethoven’s 5^th symphony
> randomly strike keys on your keyboard as if you were playing the
> symphony and your code should just report invalid input and not an
> exception). If we had a readInt and/or readDbl they would not pass
> this test. That Python would happily accept anything and then fail
> when the value was used in a calculation is no better. But, for
> learning Java could we have a readInt or readDbl alongside readln? I
> know that DataInputStream has such methods, but without a prompt and
> it must be attached to an input stream such as a file.
>
> As always, just thinking out loud. Feel free to use my name in vain.
>
> Ken
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20241107/3fc311c3/attachment-0001.htm>
More information about the amber-dev
mailing list