This question already has answers here:
Quick alternative to lots of if statements
(5 answers)
Closed 2 years ago.
I am trying to write code to let the user pick addition, subtraction, multiplication or division. Once the user has picked one, the program will ask them what difficulty they want, etc. Instead of writing something like:
if (operation.equals("1") || operation.equals("addition")){
if(level.equals("1")) {
} else if (level.equals("2")){
}
} else if (operation.equals("2") || operation.equals("subtraction")) {
if(level.equals("1")) {
} else if (level.equals("2")){
}
} else if (operation.equals("3") || operation.equals("multiplication")) {
if(level.equals("1")) {
} else if (level.equals("2")){
}
} else if (operation.equals("4") || operation.equals("division")) {
if(level.equals("1")) {
} else if (level.equals("2")){
}
}
And so on.
Is there an easier way to write something like this? Instead of writing a bunch of if statements?
Try a switch, for your example it would look something like this.
switch(operation) {
case "1":
case "addition":
if(level.equals("1")) {
} else if (level.equals("2")){
}
break;
case "2":
case "subtraction":
...
break;
case "3":
.
.
.
default:
...
}
default is used when none of the other cases are true.
Here is a link to a tutorial if the answer wasn't helpful.
https://www.w3schools.com/java/java_switch.asp
You could map the string into an enumeration value and the use a switch / case.
import java.util.Map;
import java.util.HashMap;
class TestMain {
public enum OPERATOR_TYPE { ADDITION, SUBTRACTION, MULTIPLICATION, DIVISION };
public static Map<String, OPERATOR_TYPE> lookup = buildMap();
public static Map<String, OPERATOR_TYPE> buildMap() {
Map<String, OPERATOR_TYPE> rv = new HashMap<String, OPERATOR_TYPE>();
rv.put("addition", OPERATOR_TYPE.ADDITION);
rv.put("subtraction", OPERATOR_TYPE.SUBTRACTION);
rv.put("multiplication", OPERATOR_TYPE.MULTIPLICATION);
rv.put("division", OPERATOR_TYPE.DIVISION);
rv.put("1", OPERATOR_TYPE.ADDITION);
rv.put("2", OPERATOR_TYPE.SUBTRACTION);
rv.put("3", OPERATOR_TYPE.MULTIPLICATION);
rv.put("4", OPERATOR_TYPE.DIVISION);
return rv;
}
public static void main(String[] args) {
String opStr = System.console().readLine();
OPERATOR_TYPE op = lookup.get(opStr.toLowerCase());
switch (op) {
case ADDITION:
System.out.println("Addition");
break;
case SUBTRACTION:
System.out.println("Subtraction");
break;
case MULTIPLICATION:
System.out.println("Multiplication");
break;
case DIVISION:
System.out.println("Division");
break;
}
}
}
Related
I'm in the process of learning Java 8 and I wanted to know if there was a clean way to re-write the below code more efficiently in Java 8:
public static Map<String, Character> parseOrg(String org) {
Map<String, Character> map = new HashMap<String, Character>();
if (org != null && !org.isEmpty()) {
String modifiedString = trimOrg(org); //private method to substring
if (modifiedString.length() == 4) {
populateMap(modifiedString.charAt(modifiedString.length()-1), modifiedString.charAt(modifiedString.length()-2), modifiedString.charAt(modifiedString.length()-3), modifiedString.charAt(modifiedString.length()-4));
} else if (modifiedString.length == 3) {
populateMap(modifiedString.charAt(modifiedString.length()-1), modifiedString.charAt(modifiedString.length()-2), modifiedString.charAt(modifiedString.length()-3), null);
} else if (modifiedString.length == 2) {
populateMap(modifiedString.charAt(modifiedString.length()-1), modifiedString.charAt(modifiedString.length()-2), null, null);
} else if (modifiedString.length == 1) {
populateMap(modifiedString.charAt(modifiedString.length()-1), null, null, null);
}
} else {
LOG.error("Null org provided");
}
return map;
}
private static void populateMap(Map<String, Character> map, Character pos0, Character pos1, Character pos2, Character pos3) {
map.put("Position 3", pos3);
map.put("Position 2", pos2);
map.put("Position 1", pos1);
map.put("Position 0", pos0);
}
You could do something like this:
Runnable r = Arrays.asList(() -> {}, YourClass::doD, YourClass::doC, YourClass::doB, YourClass::doA, () -> {})
.get(Math.min(Math.max(0, org.length()), 4));
r.run();
But honestly, it's not worth it. Just use a switch.
Your question is strange because it appears to call the same doX() method every time. If that is really what you want, you can have a simple if-statement and supply it a range:
if(modifiedString.length() >= 1 && modifiedString.length() <= 4) {
doX();
}
If indeed you meant that they are different methods and do different things depending on the modifiedString.length value, you can use a switch-statement, as suggested by others:
switch (modifiedString.length()) {
case 1:
doA();
break;
case 2:
doB();
break;
case 3:
doC();
break;
case 4:
doD();
break;
default:
//if no case match
}
I have an assignment to build a game bord...
I want the user to put the in program the keys he wants to play with.
The problem is that Java requires that the values in the switch case will be const...
The idea is to create a type inheritance to the KeyListener and then I just add it to his proper player.
the desired Keys are received as a parameter int[] keys
I've seen some people offering a solution for similar problems by adding "static final" unfortunately it does not help
here is my code :
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class PlayerKeyListener implements KeyListener
{
int[] keys;
int playerID;
PlayerKeyListener(int[] keys, int playerID)
{
this.keys = keys;
this.playerID = playerID;
}
public void keyPressed(KeyEvent e)
{
System.out.println("keyPressed code : "+ e.getKeyCode());
switch (e.getKeyCode())
{
case keys[KeyboardSettings.UP]: gameBord.players[playerID].moveUp(); break;
case keys[KeyboardSettings.DOWN]: gameBord.players[playerID].moveDown(); break;
case keys[KeyboardSettings.LEFT]: gameBord.players[playerID].moveLeft(); break;
case keys[KeyboardSettings.RIGHT]: gameBord.players[playerID].moveRight(); break;
case keys[KeyboardSettings.BOMB]: gameBord.players[playerID].addBomb(); break;
default: gameBord.players[playerID].face = 0;
}
e.consume();
}
public void keyReleased(KeyEvent e)
{
System.out.println("keyReleased code : "+ e.getKeyCode());
gameBord.players[playerID].move = false;
gameBord.players[playerID].pic = 0;
e.consume();
}
public void keyTyped(KeyEvent e)
{
System.out.println("keyTyped code : "+ e.getKeyCode());
e.consume();
}
}
tnx ;)
Case expressions must be constant expressions.
You answered yourself in the title. You can't have a variable after case keyword.
The way how switch command works is that all the cases (the values after the case keyword) must be known in compile-time.
switch (e.getKeyCode()) {
case constant1: commands;
case constant2: commands;
}
keys[KeyboardSettings.UP] is not a constant, it is a variable. Its value is known in run-time. The compiler does not know what value is there and is shouting at you: "Hey, user3142930, I need to know the value of everything which follow the case keyword!"
In your case you simply cannot use switch. You should use a sequence of if-else commands, like this:
// store it in a variable so you do not call the method repeatedly
final int code = e.getKeyCode();
// this is instead of your switch
if (code == keys[KeyboardSettings.UP]) {
....
}
else if (code == keys[KeyboardSettings.DOWN]) {
...
}
else if (...) {
...
}
The deep reason for it is that you want to have the keys user-configurable, so you simply cannot know their values in compile-time.
You may study Runtime vs Compile time if you have not understood my last sentence.
I would like to use a switch statement, but I am not able to construct it without either duplicating code or using an accompanying if statement. Is there a way around this?
I have 5 cases, and for all but one of them I would like to perform a certain action. So with a switch statement, I can just do:
switch(x) {
case A:
foo();
break;
case B:
case C:
case D:
case E:
bar();
break;
}
Easy. But the difficulty comes in that I also need to perform another distinct action for each one, so I can't use the fall-through feature of the cases. So I'm reduced to either
switch(x) {
case A:
foo();
baz(0);
break;
case B:
bar();
baz(1);
break;
case C:
bar();
baz(2);
break;
case D:
bar();
baz(3);
break;
case E:
bar();
baz(4);
break;
}
which smells to me because of having to repeat bar() every time, or
switch(x) {
case A:
baz(0);
break;
case B:
baz(1);
break;
case C:
baz(2);
break;
case D:
baz(3);
break;
case E:
baz(4);
break;
}
if (x != A) { bar(); }
which doesn't duplicate any code, but it bothers me that there I need to use both switch and if.
I guess one other alternative would be to use a map, like
Map<X, Integer> m = new HashMap<X, Integer>();
m.put(A, 0);
m.put(B, 1);
m.put(C, 2);
m.put(D, 3);
m.put(E, 4);
if (m.get(x) == 0) {
foo();
} else {
bar();
}
baz(m.get(x));
but now I've introduced a whole data structure just to clean this up. (And when you count the initialization of the map, it's not even that much cleaner.)
Any tips?
Is x by any chance an enum? In which case just move the method to the enum instead of switching.
enum Employee {
SENIOR {
#Override
public int salary() {
return 60;
}
},
JUNIOR {
#Override
public int salary() {
return 40;
}
};
public abstract int salary ();
}
And calling
employee.salary();
Is much better than switching.
Yes; you will have to duplicate method calls, but I think this is correct and clear. Or... use a constructor in your enum. Excuse contrived mix of "Employee" code with "foobar" code.
private final boolean flag;
Employee(int flag) {
this.flag = flag;
}
public int method() {
if(flag) {
secondMethod();
}
alwaysMethod();
}
I would split logic by pass x to baz and create new switch there:
switch(x) {
case A:
foo();
break;
case B:
case C:
case D:
case E:
bar();
baz(x); // < ---
break;
}
void baz(SomeEnum val){
switch(val) {/* ...*/}
}
I guess your cases are fixed so you can use enum and just exclude one of them.
public enum MyCase {
A, B, C, D, E;
}
and the condition
MyCase x;
...
if MyCase.A.equals(x) {
foo();
} else {
bar();
}
baz(x);
// or if the oridnal value is required
baz(x.ordinal);
I have the below piece of code that I have developed ..
public byte determineCardType(final IInput inputData) {
byte cardType = UNKNOWN_CARD;
try {
if (isWagRewardsLoyaltyCard(inputData))
cardType = WAG_LOYALTY_CARD_TYPE;
else if (isDRCard(inputData)) //checking that card scanned and swiped is a DR Card
cardType = DR_CARD_TYPE;
else if (isWagRewardsPartnerCard(inputData))
cardType = AARP_CARD_TYPE;
return cardType;
} catch (Exception e) {
return UNKNOWN_CARD;
}
}
please advise me can the above code can be adjusted in switch loop, if yes then please advise, Thanks in advance.
the other code that is linked to it...
if((aarpCardSupport.isAARPCard(input))||(determineCardType(input)==DR_CARD_TYPE)) {
return true;
} else if((isDRCard(input))&&(isDRLoayltyEnabled())) { //would return 1 for DR card only when isDRLoayltyEnabled returns true
return true;
}
return false ;
Switch won't help you, but conditional expressions can:
return isWagRewardsLoyaltyCard(inputData) ? WAG_LOYALTY_CARD_TYPE
: isDRCard(inputData) ? DR_CARD_TYPE
: isWagRewardsPartnerCard(inputData) ? AARP_CARD_TYPE
: UNKNOWN_CARD;
I would strongly recommend against catching Exception, btw. Catch specific exception types if you must - although in that case I'd at least log the error before returning.
EDIT: As for the second piece of code, it looks like you just want:
return aarpCardSupport.isAARPCard(input)
|| determineCardType(input) == DR_CARD_TYPE
|| (isDRCard(input) && isDRLoayltyEnabled);
Although why determineCardType(input) would return something other than DR_CARD_TYPE when isDRCard(input) returns true is a mystery...
Don't do it. If you must do it, you could do it like this:
public byte determineCardType (final IInput inputData) {
byte cardType = UNKNOWN_CARD;
try {
switch (isWagRewardsLoyaltyCard (inputData) ? 1 :
(isDRCard (inputData) ? 2 : (isWagRewardsPartnerCard (inputData) ? 3 : 4)))
{
case 1: cardType = WAG_LOYALTY_CARD_TYPE; break;
case 2: cardType = DR_CARD_TYPE; break;
case 3: cardType = AARP_CARD_TYPE; break;
default: // UNKNOWN_CARD already default
}
return cardType;
} catch (CardException ce) {
return UNKNOWN_CARD;
}
}
}
But since you return a cardType as byte, you can switch later, after setting the type. This code is harder to read than your code. Jons code is at least lean.
I see you're new to switch statements. So I elaborate a little.
A switch statement is useful if your input is in Integral form (int, byte, short, Enum), and if you can match it against a - mostly medium short list of values.
And if you otherwise would use a sequence of if/else statements. Case statements needn't be mutual exclusive, but most times they are.
In your case, you call 3 methods which return booleans, so the input isn't integral. But your output is.
Later in the code, you could write:
byte cardType = determineCardType (inputDate);
switch (cardType)
{
case WAG_LOYALTY_CARD_TYPE: doWlct (foo, bar); break;
case DR_CARD_TYPE: doDct (); break;
case AARP_CARD_TYPE: doAct (baz); foobar (); break;
case UNKNOWN_CARD: System.err.println ("Unknown card");
}
Else you would have to write:
if (cardType == WAG_LOYALTY_CARD_TYPE)
doWlct (foo, bar);
else if (cardType == DR_CARD_TYPE)
doDct ();
else if (cardType == AARP_CARD_TYPE) {
doAct (baz);
foobar ();
}
else ...
Since Java 1.7 switch over Strings is possible as well, but until then, you could only switch over integral types, and the case labels have to be literally constant and known at compile time.
In OOP, switch statements are often abandoned in favor of dynamic dispatch.
interface Card {
public abstract int authenticate ();
}
class WagLoyalityCard implements Card {
public int authenticate () {
doWlct (foo, bar);
}
}
class DrCard implements Card {
public int authenticate () {
doDct ();
}
}
// ... and so on
Card card = new DrCard ();
// authenticate:
card.authenticate ();
// depending on the card type, DrCard.authenticate () or
// WagCard.authenticate () gets called.
I need to change the following if's to a switch-case while checking for a String, to improve the cyclomatic complexity.
String value = some methodx;
if ("apple".equals(value)) {
method1;
}
if ("carrot".equals(value)) {
method2;
}
if ("mango".equals(value)) {
method3;
}
if ("orange".equals(value)) {
method4;
}
But I am not sure what value I'm going to get.
Java (before version 7) does not support String in switch/case. But you can achieve the desired result by using an enum.
private enum Fruit {
apple, carrot, mango, orange;
}
String value; // assume input
Fruit fruit = Fruit.valueOf(value); // surround with try/catch
switch(fruit) {
case apple:
method1;
break;
case carrot:
method2;
break;
// etc...
}
Everybody is using at least Java 7 now, right? Here is the answer to the original problem:
String myString = getFruitString();
switch (myString) {
case "apple":
method1();
break;
case "carrot":
method2();
break;
case "mango":
method3();
break;
case "orange":
method4();
break;
}
Notes
The case statements are equivalent to using String.equals.
As usual, String matching is case sensitive.
According to the docs, this is generally faster than using chained if-else statements (as in cHao's answer).
Learn to use else.
Since value will never be equal to two unequal strings at once, there are only 5 possible outcomes -- one for each value you care about, plus one for "none of the above". But because your code doesn't eliminate the tests that can't pass, it has 16 "possible" paths (2 ^ the number of tests), of which most will never be followed.
With else, the only paths that exist are the 5 that can actually happen.
String value = some methodx;
if ("apple".equals(value )) {
method1;
}
else if ("carrot".equals(value )) {
method2;
}
else if ("mango".equals(value )) {
method3;
}
else if ("orance".equals(value )) {
method4;
}
Or start using JDK 7, which includes the ability to use strings in a switch statement. Course, Java will just compile the switch into an if/else like construct anyway...
To reduce cyclomatic complexity use a map:
Map<String,Callable<Object>> map = new HashMap < > ( ) ;
map . put ( "apple" , new Callable<Object> () { public Object call ( method1 ( ) ; return null ; } ) ;
...
map . get ( x ) . call ( ) ;
or polymorphism
Just to make concrete emory's answer, the executable code is the following :
Map<String,Callable<USer>> map = new HashMap<String,Callable<User>>();
map.put( "test" , new Callable<User> () { public User call (){ return fillUser("test" ); }} ) ;
map.put( "admin" , new Callable<Utente> () { public Utente call (){ return fillUser("admin" ); }} ) ;
where user is a POJO, and then
User user = map.get(USERNAME).call();
finally the called method is somewhere :
private User fillUser(String x){
User user = new User();
// set something in User
return user;
}
Java does not support Switch-case with String. I guess this link can help you. :)
Here is a possible pre-1.7 way, which I can't recommend:
public class PoorSwitch
{
final static public int poorHash (String s) {
long l = 0L;
for (char c: s.toCharArray ()) {
l = 97*l + c;
}
return (int) l;
}
public static void main (String args[])
{
String param = "foo";
if (args.length == 1)
{
param = args[0];
}
// uncomment these lines, to evaluate your hash
// test ("foo");
// test ("bar");
switch (poorHash (param)) {
// this doesn't work, since you need a literal constant
// so we have to evaluate our hash beforehand:
// case poorHash ("foo"): {
case 970596: {
System.out.println ("Foo!");
break;
}
// case poorHash ("bar"): {
case 931605: {
System.out.println ("Bar!");
break;
}
default: {
System.out.println ("unknown\t" + param);
break;
}
}
}
public static void test (String s)
{
System.out.println ("Hash:\t " + s + " =\t" + poorHash (s));
}
}
Maybe you could work with such a trick in a generated code. Else I can't recommend it. Not so much that the possibility of a hash collision makes me worry, but if something is mixed up (cut and paste), it is hard to find the error. 931605 is not a good documentation.
Take it just as proof of concept, as curiosity.
We can apply Switch just on data type compatible int :short,Shor,byte,Byte,int,Integer,char,Character or enum type.
Evaluating String variables with a switch statement have been implemented in Java SE 7, and hence it only works in java 7. You can also have a look at how this new feature is implemented in JDK 7.
Java 8 supports string switchcase.
String type = "apple";
switch(type){
case "apple":
//statements
break;
default:
//statements
break; }
String name,lname;
name= JOptionPane.showInputDialog(null,"Enter your name");
lname= JOptionPane.showInputDialog(null,"Enter your father name");
if(name.equals("Ahmad")){
JOptionPane.showMessageDialog(null,"welcome "+name);
}
if(lname.equals("Khan"))
JOptionPane.showMessageDialog(null,"Name : "+name +"\nLast name :"+lname );
else {
JOptionPane.showMessageDialog(null,"try again " );
}
}}
Not very pretty but here is another way:
String runFct =
queryType.equals("eq") ? "method1":
queryType.equals("L_L")? "method2":
queryType.equals("L_R")? "method3":
queryType.equals("L_LR")? "method4":
"method5";
Method m = this.getClass().getMethod(runFct);
m.invoke(this);
String value = someMethod();
switch(0) {
default:
if ("apple".equals(value)) {
method1();
break;
}
if ("carrot".equals(value)) {
method2();
break;
}
if ("mango".equals(value)) {
method3();
break;
}
if ("orance".equals(value)) {
method4();
break;
}
}