Improving methods in Java - java

I have recently started a course where the main language we are learning at the moment is Java.
I have been tasked with creating a program that allows people to vote on two candidates - the program then counts the votes, and depending on how man votes have been made depends on what is displayed.
Here is the part I am concerned with at the moment:
public String printResults(){
if(candidate1Votes == 0 && candidate2Votes == 0)
{
System.out.println ("No votes cast, results cannot be displayed.");
return "No votes cast, results cannot be displayed.";
}
else if(this.completed == false)
{
System.out.println ("Voting has not finished");
return "Voting has not finished";
}
else if(this.completed == true)
{
System.out.println ("Voting has finished, no more votes will be allowed.");
return "Voting has finished, no more votes will be allowed";
}
{
double totalVotes = this.candidate1Votes + this.candidate2Votes;
double cand1Share = (double) this.candidate1Votes/totalVotes*100;
double cand2Share = (double) this.candidate2Votes/totalVotes*100;
System.out.format(candidate1 + " received %3.1f percent of the votes\n", cand1Share);
System.out.format(candidate2 + " received %3.1f percent of the votes\n", cand2Share);
return "v";
}
}
Originally I used void in this method, but part of our task was to then change it to a string value. This is where I am struggling - once I set completed to true, it is still allowing me to cast votes. I know that this code is incomplete but I can't finish it as I am unsure what to do! These were the next parts to the questions.
Modify your printResults method so that it applies the first two rules. Note that the value of the completed field indicates whether or not voting is complete. The method should be modified to return a String which indicates whether printing has been successful.
Modify your vote method to apply the third rule.
Test your methods by creating an instance and doing the following – before
doing each test note the result you expect to get, and compare this with what you actually get:
• Try to print results immediately
• Cast votes for both candidates and try to print results
• Set the completed field to true by calling setCompleted
• Try to cast a vote for a candidate
• Print the results
I am new to this (this is my first year) and have managed to do okay in my books to get this far, however any help on this next issue would be greatly appreciated!

First of your code is unnecessary complicated, which makes it hard to read/enhance. It can easily simplified, like
public String printResults(){
if(candidate1Votes == 0 && candidate2Votes == 0) {
System.out.println ("No votes cast, results cannot be displayed.");
return "No votes cast, results cannot be displayed.";
} // you returned ... NO need for ELSE!
if(this.completed == false) {
System.out.println ("Voting has not finished");
return "Voting has not finished";
}
// it is very clear here that completed must be true!
double totalVotes = this.candidate1Votes + this.candidate2Votes;
double cand1Share = (double) this.candidate1Votes/totalVotes*100;
double cand2Share = (double) this.candidate2Votes/totalVotes*100;
System.out.format(candidate1 + " received %3.1f percent of the votes\n", cand1Share);
System.out.format(candidate2 + " received %3.1f percent of the votes\n", cand2Share);
return "v";
}
Probably that easier-to-read code is all that you need to get you going!

Looking at the code the last block will never be reached because either you have no votes or you have votes and in that case completed will be either true or false and will thus reach always one of the else ifs and they all return a string. So I wonder why how you can cast any votes at all.
You could also post the code where you call printResults and setCompleted to see where the problem lies.
Some more hints for improving your code:
Sometimes you have the opening bracket on the same line and sometimes on the next. You should probably choose one style
It is not necessary to surround the last code block with brackets
if (this.completed == true) and else if (this.completed == false) is a bit redundant and can be written like: if (this.completed) and if (!this.completed). Also you can write
if (this.completed) {
...
} else {
....
}
because if completed is not true it can only be false.
Instead of writing every String two times and having to edit it two times in case you want to change something you could also do the following:
String msg = "Voting has not finished"
System.out.println(msg);
return msg;

Related

Function not ending correctly after recursive loop

I am trying to build a copy cat of the game risk. I have a while loop which says while the attack isn't finished do something. Then I ask the user to type in either 'end turn' to end turn or 'continue' to recursively call the attack function again. The problem is after the user types in attack a few times and then 'end turn' the turn doesn't end rather it starts from the beginning or the function again. I would greatly appreciate an expert eye to look at my code and see what I am missing, thanks in advance.
public void attackOrSkip(Player player,Player[] playerArray, int playerId) {
boolean attackFinished = false;
int numUnitsAttackWith = 0;
int defenceArmiesNumber =0;
displayString(makeLongName(player) + ": Type 'attack' to attack or 'skip' to skip your turn...");
String command = commandPanel.getCommand();
displayString(PROMPT + command);
if(command.equals("skip") ||command.equals("skip ") ||command.equals("s")) {
return;
}else if (command.equals("attack") ||command.equals("attack ")){
displayString(PROMPT + command);
//while the attack isn't finished
while(attackFinished == false) {
//get the country the user is attacking
int countryAttackingFrom=countryFromCheck(playerId,player);
//get the country to attack
int countryToAttack = countryToCheck(player);
//get the player who we are attacking
int occupierPlayer =board.getOccupier(countryToAttack);
if ((board.getNumUnits(countryAttackingFrom)) < 2) {
displayString("You dont have enough units on this country to make an attack!");
attackOrSkip(player, playerArray, playerId);
break;
}
//if the country is adjacent to another one then you can attack else no
else if(isAdjacent(countryAttackingFrom,countryToAttack)) {
//check the number of unit to attack with
numUnitsAttackWith =numUnitsCheckerAttack(player,countryAttackingFrom);
//check the number of unit to defend with
defenceArmiesNumber = numUnitsCheckerDefence(player,countryToAttack);
//roll the dice
player.rollDice(numUnitsAttackWith);
playerArray[occupierPlayer].rollDice(defenceArmiesNumber);
//display the roll results
displayString(makeLongName(player) + "Rolled: "+printDie(player));
displayString(makeLongName(playerArray[occupierPlayer]) + "Rolled: "+printDie(playerArray[occupierPlayer]));
}
displayString(makeLongName(player) + ": 'end turn' or 'continue'");
command = commandPanel.getCommand();
displayString(PROMPT + command);
if(command.equals("end turn")||command.equals("end turn ") ||command.equals("endturn")||command.equals("endturn ") ||command.equals("end")) {
attackFinished = true;
return;
}else if(command.equals("attack") ||command.equals("attack ")){
// break;
}else if(command.equals("continue") ||command.equals("continue ") ||command.equals("con")){
attackOrSkip(player,playerArray,playerId);
}else {
return;
}
}else {
displayString(makeLongName(player) + ": ERROR, not adjacent countries");
}
}
}
}
Okay - as currently written - every time you call attackOrSkip within the method - you end up 1 level lower in the stack - with a new set of variables -
attackFinished, numUnitsAttackWith, defenceArmiesNumber
When you leave the recursion (i.e. via a return) you end up with simple variables as they were before you enter the recursive call - remember Java is Call By Value (even though you can pass in references to objects, you get a value (the current value of the variable reference when called ... and changing the reference to point at a different object doesn't change the callers reference).
SO, without looking to see whether you have done the correct algorithm - I would guess that if you made the method return type a boolean and returned the status instead of nothing, you could update attackFinished and the right thing might happen..
e.g. change all the
return;
to
return attackFinished;
AND change all the places where you call
attackOrSkip(....)
to set attack finished based on what the method return
attackFinished = attackOrSkip(....)
OR - you can pass in an extra parameter - attackFinished - in a Holder (an example of the concept) object - again the reference can't change, but you can go attackFinished.value = true (and it will then be the same the whole way out the stack as you drop out of the recursion).

WHILE repeats before ELSE is evaluated

I've nested an IF ELSE statement inside a WHILE statement, but am confused as to why the WHILE is interpreted before the ELSE (when the IF fails). A user is asked to enter a number from 1-10 (inclusive). If the number is inside that range, the program ends. If it's outside of that range, I want to display an error and then prompt them to again enter a number.
It works well if I put the "prompt" before the WHILE, but then I have to put it again inside the ELSE statement for it to show up again. I found this question, but it didn't appear to answer the issue I'm running into. I'm admittedly a Java novice, so I apologize if I'm missing some fundamental aspect of Java.
import java.util.Scanner;
public class Rangecheck {
private static int userNumber; //Number input by user
private static boolean numberOK = false; //Final check if number is valid
//String that will be reused in the DO statement
private static String enterNumber = "Please enter a number from 1 to 10: ";
public static void main(String[] args) {
//Print string
while(!numberOK) // Repeat until the number is OK
{ System.out.println(enterNumber);
Scanner input_UserNumber = new Scanner(System.in); //input integer
userNumber = input_UserNumber.nextInt();
if (10>= userNumber && userNumber >= 1) //Check if 10>=input>=1
{
/*
** If number was valid, congratulate the user and mark numberOK true
*/
System.out.println("Good job! The number you entered is "+userNumber+".");
numberOK = true; // Congratulate user / exit loop if successful
}
else ; //if (10 < userNumber && userNumber < 1)
{
System.err.println("The number entered was not between 1 and 10!");
System.err.print(enterNumber); // Error; user retries until successful
}
}
}
}
I'd expect the System.err.println() to be evaluated in the else statement and then the whileto be evaluated, so that this gets returned:
The number entered was not between 1 and 10!
Please enter a number between 1 and 10:
I've sort of worked around this by putting enterNumber just before while, then putting a second
println in the else statement immediately following the error. It returns what I expect, but I believe I'm fundamentally misunderstanding something.
Let's suppose you have the following code:
while (whileCondition) {
//Inside while, before if
if (ifCondition) {
//Inside if
} else {
//Inside else
}
}
This cycle will execute repeatedly, until the whileCondition becomes false. Whenever the ifCondition is true, the operations inside if will be executed, otherwise the operations inside else will be executed.
Back to your problem:
Your line of
System.out.println(enterNumber);
is at the start of the while. So before the code even gets to your if, the content of enterNumber is displayed on the console. Later, your if is evaluated and if you enter, let's say 22, the condition given to the if will be false and the content of the else block will be evaluated.
The else statement is repeated before the while statement. There can however sometimes be a problem with Streams. A Stream does not necessarily print the data immediately. Especially in the case one uses two different streams, the order of printing the output data can be interleaved.
You can use:
System.err.flush();
to ensure the data is written to the console first.
Add this statement in else body:
numberOk=false;

Checking values in boolean array (Java)

I am having som slight difficulties with the following problem.
I have initialized a boolean array called numberArray with 31 indexes. The user is supposed to enter 5 digits between 1 and 30, and each time a digit is entered, the program is supposed to set the proper index to true. For instance, if I enter 5 then:
numberArray[5] = true;
However, if the user enters the value 5 a second time, a message should be given to the user that this number has already been entered, and so the user has to choose a different value. I have tried to create a loop as follows:
public void enterArrayValues() {
for(int i = 1; i < 6; i++) {
System.out.print("Give " + i + ". number: ");
int enteredNumber = input.nextInt();
while (numberArray[enteredNumber] = true) {
System.out.println("This number has already been chosen.");
System.out.print("Give " + i + ". number again: ");
enteredNumber = input.nextInt();
}
numberArray[enteredNumber] = true;
}
}
The problem is that when I run the program, I automatically get the message "The number has already been chosen" no matter what I enter. Even the first time I enter a number. I don't get this. Isn't all the values in the boolean array false by default?
I would greatly appreciate it if someone could help me with this!
while (numberArray[enteredNumber] = true) {
make that
while (numberArray[enteredNumber] == true) {
or change to
while (true == numberArray[enteredNumber]) {
or simply drop the ==true
while (numberArray[enteredNumber]) {
while (numberArray[enteredNumber] = true)
is an assignment, use the == operator or simply while (numberArray[enteredNumber]).
I know its hard to get into while you are still learning, but the earlier you start coding in an IDE the better off you will be. This is one tiny example of something an IDE will warn you about.
Change the while line to:
while (numberArray[enteredNumber]) {
Because mistakenly entering = instead of == is a common mistake, some people always code this type of statement in the following manner:
while (true == numberArray[enteredNumber]) {
With this format, if you use = instead of ==, you will get a compiler error.
Also, if you use a type of static analysis tool such as PMD, I believe you get a warning for the statement that you originally wrote.
Thde problem is in the condition of the while loop - you are using the assignment operator (=), whereas you are supposed to use the equality comparer (==). This way the loop condition is always true, because you are assigning true to the indexed field.
I hope this will work :-) .
The condition in the while loop should be while (numberArray[enteredNumber] == true). You're using the assignment operator =, not the comparison operator ==. Assignment is an expression that returns the assigned value, which is true in your case.

Error with BigDecimal calculation regarding User Input

I have this idea for my assignment where I wanted a cash register system to calculate the total for an item when the user enters it's cost price and quantity of said item.
That seemed to work, but then led my main problem - I wanted to let the user type the letter "T" after say, 10 transactions, to find out the total takings for the day.
I tried to use a for loop with the BigDecimal math class within the calculations etc.
I have errors on the words 'valueOf' within my calculations & Eclipse keeps trying to change my values to 'long' & i'm pretty sure that's not right.
My explanation isnt amazing so i'll give you the code i wrote and place comments next to where my errors are ..
try{
Scanner in = new Scanner (System.in);
String t = "T";
int count;
for (count = 1;count<=10;count++){
System.out.println("\n\nValue of Item " + count + " :");
BigDecimal itemPrice = in.nextBigDecimal();
System.out.println("Quantity of item " + count + " :");
BigDecimal itemQuantity = in.nextBigDecimal();
BigDecimal itemTotal = (BigDecimal.valueOf(itemPrice).multiply // error here
(BigDecimal.valueOf(itemQuantity))); // error here
System.out.println("\nTotal for item(s): £" + itemTotal);
count++;
while (t == "T"){
BigDecimal amountOfItems = (BigDecimal.valueOf(itemTotal).divide // error here
(BigDecimal.valueOf(itemQuantity))); // error here
BigDecimal totalTakings = (BigDecimal.valueOf(itemTotal).multiply // error here
(BigDecimal.valueOf(amountOfItems))); // error here
System.out.println("The Total Takings For Today is £" + totalTakings + " " );
}
}
}
}
}
Like I said, the 'red lines' that eclipse uses to show there is an error are only under the words, "valueOf" within my BigDecimal calculations.
Any help would be great because i'm tearing my hair out !!!!
Thanx,
Vinnie.
There's no method BigDecimal.valueOf(BigDecimal). itemPrice and itemQuantity are already BigDecimal values - you don't need any conversion:
BigDecimal itemTotal = itemPrice.multiply(itemQuantity);
EDIT: Okay, so the above solves your immediate problem, but you've also got:
while (t == "T")
{
// Loop that never changes the value of t
}
There are two issues with this:
The loop will always either execute forever, not execute at all, or keep going until an exception is thrown, because the loop condition can never change as you're not changing the value of t. My guess is you want t = System.in.readLine() at some point...
You're comparing two string references here whereas I suspect you want to compare their values, e.g.
while (t.equals("T"))

First Java program (calculator) problems

I'm in the process of learning Java and my first project is a calculator, however I've run into a snag. I'm trying to get my calculator to let me enter a number then click an operator (+, -, x, /), enter another number then hit an operator again and have the display update and be able to keep this going.
Example, I would like to be able to hit the following and have it display the total each time I hit an operator after the first:
a + b / c - d =
The code I have seems (to me) like it should work but it doesn't. What am I doing wrong?
The following is the code I'm using when you hit an operator. By default wait is set to false. After running through the class once, value1 is stored and wait is set to true and that works fine. From there it doesn't seem to work quite right:
class OperatorListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
String input = event.getActionCommand();
// Set display as string
String s = display.getText();
if (!wait) {
// Convert first input string to double
try {
value1 = Double.valueOf(s.trim()).doubleValue();
} catch (NumberFormatException nfe) {
System.out.println("NumberFormatException: " + nfe.getMessage());
}
dec = false;
} else {
// Convert second input string to double
try {
value2 = Double.valueOf(s.trim()).doubleValue();
} catch (NumberFormatException nfe) {
System.out.println("NumberFormatException: " + nfe.getMessage());
}
// Determine operation to be performed
if (operator == "add") {
value1 = Operators.add(value1, value2);
} else if (operator == "subtract") {
value1 = Operators.subtract(value1, value2);
} else if (operator == "multiply") {
value1 = Operators.multiply(value1, value2);
} else if (operator == "divide") {
value1 = Operators.divide(value1, value2);
}
// Convert final value to string and display
display.setText(Double.toString(value1));
dec = false;
}
// Determine operator hit
if (input.equals("+")) {
operator = "add";
} else if (input.equals("-")) {
operator = "subtract";
} else if (input.equals("x")) {
operator = "multiply";
} else if (input.equals("/")) {
operator = "divide";
}
// Set wait
wait = true;
}
}
EDIT: Updated code to fix some confusion and update the if statement. Even after this the same problem still exists. Also, the full source is available here
A few suggestions.
First, I would suggest when using a boolean as a conditional for an if statement, avoid comparison with true and false -- there are only two states for boolean anyway. Also, since there are only two states, rather than using else if (false), an else will suffice:
if (condition == true)
{
// when condition is true
}
else if (condition == false)
{
// when condition is false
}
can be rewritten as:
if (condition)
{
// when condition is true
}
else
{
// when condition is false
}
Second, rather than comparing the string literals "add", "subtract" and such, try to use constants (final variables), or enums. Doing a String comparison such as (operator == "add") is performing a check to see whether the string literal "add" and the operator variable are both refering to the same object, not whether the values are the same. So under certain circumstances, you may have the operator set to "add" but the comparison may not be true because the string literal is refering to a separate object. A simple workaround would be:
final String operatorAdd = "add";
// ...
if (input.equals("+"))
operator = operatorAdd;
// ...
if (operator == operatorAdd)
// ...
Now, both the assignment of operator and the comparison of operator both are referecing the constant operatorAdd, so the comparison can use a == rather than a equals() method.
Third, as this seems like the type of calculator which doesn't really require two operands (i.e. operand1 + operand2), but rather a single operand which is acting upon a stored value (i.e. operand + currentValue), it probably would be easier to have some variable that holds the current value, and another variable that holds the operator, and a method which will act according to the current operator and operand. (More or less an idea of an accumulator machine, or 1-operand computer.)
The basic method of operation will be:
Set the currentValue.
Set the operator.
Set the operand.
Perform the calculation.
Set the currentValue to the result of the calculation.
Set the operator to blank state.
Each step should check that the previous step took place -- be sure that an operation is specified (operator is set to a valid operator), then the next value entered becomes the operand. A calculator is like a state machine, where going from one step to another must be performed in a certain order, or else it will not proceed to the next step.
So, the calculator may be like this (pseudocode!):
// Initialize calculator (Step 1)
currentValue = 0;
operand = 0;
operator = operatorNone;
loop
{
operand = getOperand(); // Step 2
operator = getOperator(); // Step 3
// Step 4 and 5
if (operator == operatorAdd)
currentValue += operand;
if (operator == operatorSubtract)
currentValue -= operand;
// ...
// Step 6
operator = operatorNone;
}
Although the above code uses a single loop and doesn't work like a event-based GUI model, but it should outline the steps that it takes to run a calculator.
Whenever you enter an operator, your code will execute this:
Double.valueOf(s.trim())
for setting either value1 or value2 (depending on wait). This will throw an exception because operators can't be parsed as doubles. You might have better luck checking for the operator first, before trying to parse the input as a number. Then if it was an operator, you can skip the number parsing part.
Also consider what might happen if somebody were to enter two numbers or two operators in a row.
As Greg said, no matter what the input and no matter what the current program state, you always parse out number. You need to track the program state more cleanly. I assume that when you code has "String s = output.getText();" that you really mean "String s = input.getText();".
Also note that
if (wait == false) {
// Stuff for !wait
} else if (wait == true) {
// Stuff for wait
}
is unnecessarily redundant. You can replace it with:
if (!wait) {
// Stuff for !wait
} else {
// Stuff for wait
}
You should probably check the input string to see if it is an operator, first, and if it isn't then make sure it is numeric. Writing an infix calculator (that properly handles precedence) is not trivial.
After searching high and low I finally determined that the problem didn't lie within the code I provided. I had had a "wait = false;" in my NumberListener class that was screwing up the execution. To solve this I created 2 separate wait variables and all is working fine so far.
Thanks for the help and the tips guys, +1 to all of you for trying.
You could use the scripting engine in Java. If you don't have Java 6+, you can use Rhino which does the same thing. You can then do pretty much anything you can do in JavaScript
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
// expose a, b, c, d
engine.put("a", 1);
engine.put("b", 8);
engine.put("c", 2);
engine.put("d", 3);
// evaluate JavaScript code from String
Number value = (Number) engine.eval("a + b / c * d");
System.out.println(value);
For more examples

Categories

Resources