Need advice on code java efficiency - java

I am new to java programming and I just created a calculator program and it seems to work fine but other programmers seem to use "parsing a lot" in their calculator programs. Just wanted to ask whether I'm taking a wrong approach and might run into problems in the future using this kind of logic.Thanks.
class Refresh {
private final String a;
private final String b;
private final String c;
private final String d;
private double x, y;
public Refresh() {
a = "Enter the first number: ";
b = "Enter the function; ";
c = "Enter the second number: ";
d = "The result is: ";
}
public void types() {
Scanner typ = new Scanner(System.in);
try {
System.out.println(a);
x = typ.nextDouble();
} catch (InputMismatchException e) {
System.out.println("Please use only numbers!");
System.exit(1);
}
System.out.println(b);
String func = typ.next();
try {
System.out.println(c);
y = typ.nextDouble();
} catch (InputMismatchException e) {
System.out.println("Please use only numbers!");
System.exit(1);
}
switch (func) {
case "+":
System.out.println(d + (x + y));
break;
case "-":
System.out.println(d + (x - y));
break;
case "/":
if (y == 0) {
System.out.println("Cannot divide by zero!");
} else {
System.out.println(d + (x / y));
}
break;
case "*":
System.out.println(d + (x * y));
break;
default:
System.out.println(func + " is not valid function");
}
}
public static void main(String[] args) {
Refresh fin = new Refresh();
fin.types();
}
}

Your program is fine as a demo or as a step in learning Java, but you probably would not use it as a production program. The reason is that your code assumes a command line interface (static void main(), Scanner, System.out.println()).
Users are used to graphical solutions nowadays. They expect the input and output to be given in a graphical front end and the calculations should be done in separate logic.
In this view, I would restructure your program in 3 parts:
A back end class which does the calculations (the Calculator class in the following sample code)
A front end class (called SimpleFrontEnd below) which builds on your code, but could later be replaced with e.g. a web interface
Data objects to communicate between back end and front end (the Calculation class below to send info from the front end to the back end)
Having these 3 parts, you can modify them independently of each other. You could decide that in the front end you only enter a single String, which is then parsed before being sent to the back end.
I would probably NOT use a single String in the front end that needs to be parsed, but a JSON object that maps directly to the Calculation class below, for the following reason: the front end can easily modify the operands and operator of the JSON object independently of each other, whereas modifying a string that has to be parsed is more complex.
Here is sample code that separates front end from back end:
public class Calculation {
private double leftOperand;
private String operator;
private double rightOperand;
public double getLeftOperand() {
return leftOperand;
}
public void setLeftOperand(double leftOperand) {
this.leftOperand = leftOperand;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public double getRightOperand() {
return rightOperand;
}
public void setRightOperand(double rightOperand) {
this.rightOperand = rightOperand;
}
}
public class Calculator {
public double calculate(Calculation calculation) {
switch (calculation.getOperator()) {
case "+":
return calculation.getLeftOperand() + calculation.getRightOperand();
case "-":
return calculation.getLeftOperand() - calculation.getRightOperand();
case "/":
if (calculation.getRightOperand() == 0) {
throw new IllegalArgumentException("Cannot divide by zero!");
}
return calculation.getLeftOperand() / calculation.getRightOperand();
case "*":
return calculation.getLeftOperand() * calculation.getRightOperand();
default:
throw new IllegalArgumentException(String.format("%s is not valid function", calculation.getOperator()));
}
}
}
public class SimpleFrontEnd {
public static void main(String[] args) {
try {
//1. input, could be later replaced with a front end
Scanner typ = new Scanner(System.in);
System.out.println("Enter the first number: ");
double x = typ.nextDouble();
System.out.println("Enter the function: ");
String func = typ.next();
System.out.println("Enter the second number: ");
double y = typ.nextDouble();
//2. store input in an data object that will be sent to the back end (later on, a web interface could send this as a JSON)
Calculation calculation = new Calculation();
calculation.setLeftOperand(x);
calculation.setOperator(func);
calculation.setRightOperand(y);
//3. retrieve the result from the back end
Calculator calculator = new Calculator();
try {
double result = calculator.calculate(calculation);
System.out.println(String.format("The result is: %f", result));
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
} catch (InputMismatchException e) {
System.out.println("Please use only numbers!");
}
}
}

Parsing just means to scan a group of characters and then separate them into their own structures (tokenize them).
String aStr = "10";
The variable aStr would be of type String. You could then parse the string.
int aInt = Integer.parseInt(aStr);

Just for your information, Java has a built in calculator library.
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("JavaScript");
String result = "100/10";
System.out.println(scriptEngine.eval(result));

Alright, this is actually very basic example. And honestly you did a great job. and even better at asking this question at this time. Usually people ignore efficiency during the learning process and get too late.
Why do they do parsing?
If you donot know about the below, then you should probably try reading more on it.
Scanner reads all the key inputs from users. Lets take example
Enter a number> 12
Scanner will hold 12\n
Now you see that? scanner is holding on to just number it is actually hold on to a string. which contains 12 and \n
When you ask scanner nextDouble it will just give you 12.
But then it is still holding on to that \n so the next string input, the string \n will be assigned.
By parsing, you will ignore this issue. And you will have better control over user input.

Related

Incompatible operand types Scanner and String?

I keep getting the following errors:
Incompatible operand types Scanner
and String
Incompatible operand types int and
String
before I added the int op = Integer.valueOf(operator) line it kept giving me errors when I would name my cases. I'm still very new it's the second code I've written for my class so please keep the explanation simple if at all possible (there is also a calculator class not shown here if there's any confusion about that) :
//change the package name to calculatorClass///////////////
import java.util.Scanner;
public class assignmentB {
public static String String(int y,int x, String name, String str) throws Exception {
if (y < 1)
throw new Exception("Value must be larger than 0.");
if (x < 1)
throw new Exception("Value must be larger than 0");
return name;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Calculator c1 = new Calculator();
Scanner input = new Scanner(System.in);
c1.name = "The total is $";
try {
} catch (Exception e1){
System.out.println(e1);
}
{
int x, y, s, a, m;
String name1;
System.out.println("Red River College");
System.out.println("Custom Calculator");
System.out.println("Enter first value: ");
x = input.nextInt();
System.out.println("Enter second value: ");
y = input.nextInt();
System.out.println("Enter operation(a=Add, s=Subtract, m=multiply): ");
String operator = input.nextLine();
int op =Integer.valueOf(operator);
switch (op) {
case 1:
if (input == "s") {
System.out.println(c1.name + c1.subtract(x,y));
}
case 2:
if (input == "a") {
System.out.println(c1.name + c1.add(x,y));
}
case 3:
if (input == "m") {
System.out.println(c1.name + c1.multiply(x,y));
}
}
}
}
}
```
You have defined input to be a Scanner, with this line:
Scanner input = new Scanner(System.in);
You can then use input to do scanner things, as you're doing here:
x = input.nextInt();
So far so good.
But later in the code, you do this (and a few variations that are similar):
if (input == "s") {
...
}
This isn't valid. The Java compiler is telling you that it will not allow you to use == to compare a Scanner (that is, input) against a String (which in this example is "s"). The compiler is giving you an error to say: input == "s" isn't allowed.
It isn't clear what your intent is (that is, how you want the code to actually behave), but the issue is definitely with those three if statements. Each is attempting to compare a java.util.Scanner with a java.lang.String (one is "s", one is "a", the last is "m"). So you'll need to fix those to do whatever it is you're trying to do.
As a guess, maybe you want to read a new String input from the scanner, and then compare that to "s", etc.? If so, that could look something like below. Note that to compare strings you should use equals() (don't use == to compare strings, or any other objects).
String newString = input.next();
switch (op) {
case 1:
if (newString.equals("s")) {
System.out.println(c1.name + c1.subtract(x, y));
}
...
}

Loops and user input validation in java, again

So, I think about two days back I asked a question about input validation, and how to loop programs until the user gave a valid input..
So I made a calculator and I wanted to loop every step of the program so that if the user didn't put a double (69, 42.0) or the appropriate operator char (/, *, +, -) they would be stuck on that step till they got it right or otherwise closed the app entirely.
So one thing I got from the last question about this is that I could make a boolean value called "restart" or something and encapsulate my entire code except for the main method obviously and at the end I could make a question that they could answer true or false and the entire app could run again. While I admit having a "start over" button on my app is cool and useful for all my other a projects (probably not, I'm sure this can be done more efficiently), It still didn't satiate me and my original problem.
SO...
I got to trying everything I could (I know stack likes people who show that they at least tried).
EXAMPLE 1
Scanner input = new Scanner(System.in);
double valX;
System.out.println("Calculator Activated.");
do
{
System.out.print("Value X: ");
valX = input.nextDouble();
}while (!input.hasNextDouble());
{
System.out.println("Invalid number!");
}
//Didn't work ¯\_(ツ)_/¯
EXAMPLE 2
Scanner input = new Scanner(System.in);
double valX;
System.out.println("Calculator Activated.");
while (!input.hasNextDouble())
{
System.out.println("Value X: ");
valX = input.nextDouble();
}
//neither this one...
Note that the solution you guys give me has to apply to every step of the program.
And for a bit more clarity is the entire src code without the stuff I tried and the "restart" loop.
import java.util.Scanner;
public class CalTest
{
public static void main(String[] args)
{
Scanner input = new Scanner(System.in);
double valX, valY, divided, multiplied, added, subtracted; //Declared all my variables...
char operator;
boolean restart; //Didn't need to declare it true or false since I'm using a Scanner anyway
do //Start of the entire program
{
System.out.println("Calculator Activated.");
System.out.print("Value X: "); //I need a loop here...
valX = input.nextDouble();
System.out.print("Operator: "); //And here...
operator = input.next().charAt(0);
System.out.print("Value Y: "); //And here too..
valY = input.nextDouble();
divided = valX / valY;
multiplied = valX * valY;
added = valX + valY;
subtracted = valX - valY;
if (operator == '/')
System.out.println("Result: " + divided );
else if (operator == '*')
System.out.println("Result: " + multiplied); //<--Not sure if I need a loop with the if's
else if (operator == '+')
System.out.println("Result: " + added);
else if (operator == '-')
System.out.println("Result: " + subtracted);
else
System.out.println("Invalid operator!");
System.out.print("Try again? "); //I also need a loop here, I think.
restart = input.nextBoolean();
} while (restart); //End of it if you declared false.
System.out.println("Calculator terminated.");
}
}
At one point I tried to use the same "restart the app" concept and made a boolean variable for every single step in the code and it honestly was tiresome and not worth it.
Also I'm just a beginner if it's a concept of the loops that I'm missing then I'm happy to learn it from you guys.
Again, gracias to anyone who answers and helps contribute to my learning.
In your final code example in the class called CalTest where you assign valX = input.nextDouble(); you could a call recursive method that handles the exception until the input is what you want. Something like this:
private static double getNextDouble(Scanner input) {
try {
return input.nextDouble();
} catch (InputMismatchException e) {
System.out.println("Value X must be a number: ");
input.next();
return getNextDouble(input);
}
}
You'll replace valX = input.nextDouble(); with valX = getNextDouble(input);.
You can tidy this up and make it work for your other potential error cases, perhaps creating a parameter for the output message and passing it in as an argument.
public static void main(String[] args) {
double valX=0,valY=0;
char operator='0';//dummy default
Scanner input = new Scanner(System.in);
System.out.println("Calculator Activated.");
Double dX = null;
do
{
System.out.print("Value X: ");
dX = getDouble(input.next());
}while (dX==null);
{
valX = dX;
}
Character op = null;
do
{
System.out.print("Operator: ");
op = getOperator(input.next());
}while (op==null);
{
operator=op;
}
Double dY = null;
do
{
System.out.print("Value Y: ");
dY = getDouble(input.next());
}while (dY==null);
{
valY = dY;
}
System.out.println("Done: "+ valX + " "+operator + " " +valY);
}
static Double getDouble(String input) {
Double d = null;
try {
d = new Double(input);
}catch(NumberFormatException ex){
}
return d;
}
static Character getOperator(String input) {
Character c = null;
if("+".equals(input) || "-".equals(input) || "*".equals(input) || "/".equals(input)){
c = input.charAt(0);
}
return c;
}

Simulating multiple user input junit

I am trying to write a junit test to test a method that takes multiple user input.
The method takes a person object and sets a rating for them, the first user input is a double value for the rating, the second input is a string with the value "Y" to confirm the change.
I am trying to use ByteArrayInputStream but it is not getting the second input, the error I am getting when i run the test is No line found.
I have identified the problem as I am using different methods to validate user input I have to create a new scanner each time so the second line is not being accepted
Is there a way to set the Scanner again for the second input?
This is my test code
Person p = new Person();
double input1 = 54.3;
String input2 = "Y";
String simulatedUserInput = input1 +
System.getProperty("line.separator")
+ input2 + System.getProperty("line.separator");
System.setIn(new ByteArrayInputStream(simulatedUserInput.getBytes(StandardCharsets.UTF_8)));
addRating(p);
assertEquals(54.3, p.getMyRating(),0);
The method for adding the rating looks like this
public static void addRating(Person p)
{
double rating = Validation.validateRating("Please enter a rating for " + p.getName()); // validate input of double
boolean confirmed = Validation.validateYesNo("Are you sure you wish to set " + p.getName() + "'s rating to " + rating + " Y/N");// confirm yes no
if (confirmed)
{
p.setMyRating(rating);
}
}
Then I have a validation class to ensure correct user input,
This is for the rating
public static double validateRating(String str)
{
Scanner in = new Scanner(System.in);
double d = 0;
boolean valid = false;
while (!valid)
{
System.out.println(str);
if (!in.hasNextDouble())
{
System.out.println("Not a valid number");
in.nextLine();
} else
{
d = in.nextDouble();
if(d>=0 && d<=100)
{
valid = true;
}
else
{
System.out.println("Rating must be between 0 and 100");
}
}
}
return d;
}
this is for confirming Y/N input
public static boolean validateYesNo(String str)
{
Scanner in = new Scanner(System.in);
boolean YesNo = false;
boolean valid = false;
while (!valid)
{
System.out.println(str);
String choice = in.next();
if (choice.equalsIgnoreCase("Y"))
{
valid = true;
YesNo = true;
} else if (choice.equalsIgnoreCase("N"))
{
valid = true;
YesNo = false;
} else
{
System.out.println("Invalid input");
}
}
return YesNo;
}
You get unit testing wrong. You don't write your production code first; to later discover: this is really hard to test.
Instead: right from the beginning, you strive to write code that is as easy as possible. Ideally, you even write testcases before you write production code.
Because that helps you discovering the abstractions you need. In your case: in order to validate input values within your Person class, it should not be important, where those values are coming from.
In other words: you do not never ne jamais put System.in read calls into your production classes. You might have a test main method that reads values from the console; but you always pass such values into a method as parameter. If at all, you pass an instance Reader into your classes. You do not turn to System.in inside your methods!

Using try and catch to get the input

So I was just trying to create a simple program with bit more complicated code.
What I was asked to do was write a program which controls whether it's a positive or negative number (I know, it's easy).
What I was trying to do was to catch what was being entered, so if its not a float it will say to use a comma instead of point or enter a number instead of String.
package example1;
import java.util.InputMismatchException;
import java.util.Scanner;
public class numbritest
{
public static void main(String[] args)
{
float num;
Scanner sisse1 = new Scanner(System.in);
try
{
System.out.println("Sisesta number:");
System.out.println("Kaks komakohta on lubatud");
num = sisse1.nextFloat();
}
catch(InputMismatchException exception) //juhul kui sisestab miskit muud
{
System.out.println("Kontrolli kas kasutasid koma!");
}
if (num < 0) //kui arv väiksem
{
System.out.println("Number " +num +" on negatiivne.");
System.out.println("Seega on arv väiksem nullist");
}
else //Kui arv on suurem või võrdne
{
System.out.println("Number " +num +" on positiivne.");
System.out.println("Positiivsed arvud on suuremad");
System.out.println("või võrdsed nulliga.");
}
System.out.println();
System.out.println("Programm lõpetada!");
}
}
Sorry that it's in Estonian, but I hope you get my point.
Change float num; to float num = 0.0f; and it should work properly
What you should do is create a loop and ask user to input number as long as the number is not correct:
So this code:
try
{
System.out.println("Sisesta number:");
System.out.println("Kaks komakohta on lubatud");
num = sisse1.nextFloat();
}
catch(InputMismatchException exception) //juhul kui sisestab miskit muud
{
System.out.println("Kontrolli kas kasutasid koma!");
}
Should be in a loop - for example:
boolean incorrectNumberFormat;
do
{
incorrectNumberFormat = false;
try
{
System.out.println("Sisesta number:");
System.out.println("Kaks komakohta on lubatud");
num = sisse1.nextFloat();
}
catch(InputMismatchException exception) //juhul kui sisestab miskit muud
{
System.out.println("Kontrolli kas kasutasid koma!");
sisse1.nextLine();
incorrectNumberFormat = true; //repeat the loop;
}
} while(incorrectNumberFormat);
By the way - comma won't generate the exception (for me 3,3 doesn't), but 3%3 should give you the exception.
The loop can be created in various ways it's just an quick example.
I'd change it to
Float num = null;
(note we're now using a Float object rather than float primitive, so it can be null) and then use a while condition:
while (num == null) {
try {
//...
num = sisse1.nextFloat();
} catch (InputMismatchException e) {
//give error
}
}
This will not put anything into num until the num = ... line succeeds, so it'll stay null whenever it fails. This is better than just setting it to 0.0f, because otherwise it'll cause problems if the user enters zero.
Initialize the local varible before use;
In these case compiler consider that try block may execute or not so that num.nextFloat() may execute or not so compiler throws error: variable num might not have been initialized.
so that change float num; to float num=0.0f;

Java phonebook with stored file

I wrote a program to ask user to input names, numbers, and any notes associated. I then am trying to allow the user to search any name and have that persons info displayed. I also want to be able to store and read a file from the phone book. I am having trouble with the store/read as well as recognizing a separate class that was created. I'm guessing I just have syntax errors because I am still new to programming. Thanks for any help!
public class Entry {
public String name, num, notes;
name = a;
num = b;
notes = c;
}
import java.util.*;
import java.io.IOException;
public class Phonebook {
static int amount;
static Scanner input = new Scanner(System.in);
static file f ("FileName");
public static Entry[] pb = new Entry[200];
public static void main(String[] args) {
String ask;
char letter;
amount = 0;
do{
System.out.println("Enter a command");
ask = input.nextLine();
letter = Character.toLowerCase(ask.charAt(0));
switch (letter) {
case 'e': System.out.println(add()); break;
case 'f': System.out.println(find()); break;
case 'l': System.out.println(list()); break;
case 'h': System.out.println(help()); break;
case 'q': System.out.println("Quit"); break;
default: System.out.println("Not a valid command"); break;
}
} while (letter != 'q');
}
public static void help() {
System.out.println("Use e for enter");
System.out.println("Use f for find");
System.out.println("Use l for list");
System.out.println("Use h to see this menu again");
}
public static void add() {
String a, b, c;
System.out.println("Enter a name: ");
a = input.nextLine();
System.out.println("Enter a number: ");
b = input.nextLine();
System.out.println("Enter any notes: ");
c = input.nextLine();
}
public static void list() {
for (int i = 0; i < amount; i++)
System.out.println(pb[i].name + pb[i].num + pb[i].notes);
}
public static void find () {
String f;
boolean found;
found = false;
System.out.println("Who?");
f = input.nextLine();
for (int i = 0; i < amount; i++); {
if (f.equals((pb[i].name))) {
System.out.println(pb[i].name + pb[i].num + pb[i].notes);
found = true;
} if (!found)
System.out.println("Not found");
}
}
public static void WritePhoneBook (String f) throws Exception {
PrintStream p = new PrintStream(f);
for (int i = 0; i < amount; i++) {
p.println(pb[i].name + pb[i].num + pb[i].notes);
p.close();
System.out.println("Phonebook stored.");
}
}
}
I'm not sure how to show the errors I received but they are:
file cannot be resolved to type
i cannot be resolved
PrintStream cannot be resolved,
Syntax error at FileName,
method println(boolean) in type PrintStream is not applicable.
Since the code does not even compile, let me look at the compilation errors only (and not the logic):
static file f ("FileName");
It is File (uppercase F). In java, by convention, all classes start with a uppercase letter.
the statement is not correctly constructed. It is supposed to be
static File f = "FileName";
or something like this. Note that you need to import java.io.File as well.
System.out.println(add());
...
the methods add, find... return void. Meaning, they return nothing. System.out.println() does not operate on nothing.
PrintStream p = new PrintStream(f);
you have not imported java.io.PrintStream.
i cannot be resolved to a variable
This is because of the ; at the end of the for.
for (int i = 0; i < amount; i++) { //note: no ; after the )
if (f.equals((pb[i].name))) {
In Entry.java
the class lacks a method. Assignments like name = a; are not valid outside a method, Also a, b and c are not defined.
It looks like you are very new to Java. You may want to use a IDE like eclipse so that you get a better picture of errors and recommended fixes and you can fix them faster. However, you will learn more and learn better if you use javac from the command line (which is what I think you are doing). Of course you need to do a lot of reading and learning.
PS: Such questions will most likely be downvoted to oblivion because it shows a lack of effort from the OP's part to find solutions. I happened to have a few minutes today, but no one has the time to look into this level of detail when there seems to be no effort from OP.
OP = Original Poster = the person that asked the question

Categories