Avoid multiple if-else conditions for different enum values in Java - java

I have 6 values in the enum and using 6 if-else is really a bad practice.
Can we implement this in any better way? Below is my scenario :
ExampleEnum value = getEnumValue();
if(ExampleEnum.A == value){
doA();
}else if(ExampleEnum.B == value){
doB();
}else if(ExampleEnum.C == value){
doC();
}else if(ExampleEnum.D == value){
doD();
}else if(ExampleEnum.E == value){
doE();
}else if(ExampleEnum.F == value){
doF();
}
I was thinking of switch, but is is not making much difference also i need to return a boolean value inside doA() depending on certain parameters.
Thanks in advance.

You have a few options:
A chain of else-ifs
Leave your code as-is. Hard to read and write.
Switch
switch (value) {
case A:
doA();
break;
case B:
doB();
break;
case C:
doC();
break;
case D:
doD();
break;
case E:
doE();
break;
case F:
doF();
break;
}
Note that this is the classic switch. If you have access to newer Java versions, it is probably possible to get rid of the breaks.
EnumMap
You can also create an EnumMap:
EnumMap<ExampleEnum, Runnable> enumMap = new EnumMap<>(Map.<ExampleEnum, Runnable>of(
ExampleEnum.A, Main::doA, // 'Main', or wherever your do* methods are.
ExampleEnum.B, Main::doB,
ExampleEnum.C, Main::doC, // I'm using method references. But you could
ExampleEnum.D, Main::doD, // also use lambda expressions: '() -> doD()'.
ExampleEnum.E, Main::doE,
ExampleEnum.F, Main::doF
));
ExampleEnum value = getEnumValue();
enumMap.get(value).run();

If you want to use a switch statement and you're on Java 12 or newer, consider using extended switch expressions that avoid the pitfalls of break statements:
switch (value) {
case A -> doA();
case B -> doB();
case C -> doC();
case D -> doD();
case E -> doE();
case F -> doF();
}

You can add the do method inside the enum.
public enum ExampleEnum {
A {
public void doIt() { ... }
},
B {
public void doIt() { ... }
},
...
abstract public void doIt();
}
ExampleEnum value = getEnumValue();
if (value != null) {
value.doIt();
}

Related

Utilizing switch statements with enums and marker interface

I've got a marker interface
public interface Marker{}
and two enums which implement the Marker
public enum Sharpie implements Marker{
RED,
BLUE,
BLACK
}
public enum Crayola implements Marker{
PURPLE,
ORANGE,
GREEN
}
What I'm trying to do is utilize a switch statement, such as
public boolean isOwned(Marker m){
// Take in a marker of either Sharpie, or Crayola
switch(m){
case BLUE:
case BLACK:
case GREEN:
return true;
default:
return false;
}
}
Is there a way to do this without using an expensive instanceof call?
Something like this would work, but I'm trying to avoid using instanceof, and frankly it looks kind of ugly.
public boolean isOwned(Marker m){
// First determine instanceof and then cast the marker
// to the appropriate Type before utilizing the switch statement
if (m instanceof Sharpie){
switch((Sharpie) m){
Case BLUE:
Case BLACK:
return true;
default:
return false;
}
} else {
switch((Crayola) m){
case Green:
return true;
default:
return false;
}
}
}
Looks like a good scenario to try new Java Feature Sealed Interface and Pattern Matching for switch Expressions(* this is a preview feature as at jdk 17)
First make Marker as sealed interface
public sealed interface Marker permits Crayola, Sharpie {}
Then we can use switch expression to get rid of those instanceof checking.
public boolean isOwned(Marker marker) {
boolean isOwned = switch (marker) {
case Sharpie s -> s == Sharpie.BLACK || s == Sharpie.BLUE;
case Crayola c -> c == Crayola.GREEN;
};
return isOwned;
}
Just change the switch to if. There is no need for instanceof:
public boolean isOwned(Marker m){
if(m == Sharpie.BLUE || m == Sharpie.BLACK || m == Crayola.GREEN)
return true;
return false;
}
i don't know what are you doing. this example is using unique function for 2 diffrents enum. i think you should use extending in enum like this.

How to simplify my java code function?

How to make this code simplified? I'm having doubt on having two switch statements in my function.
private String getKeyword(String id) {
String keyword = "";
switch (id.substring(1, 2)) {
case "E":
keyword = "英語";
break;
case "M":
keyword = "数学";
break;
case "W":
keyword = "統合";
break;
}
switch (id.substring(4,5)){
case "W":
keyword = "統合";
break;
}
return keyword;
}
You could use just if else if statements.
It would be less verbose.
Besides, the before last and the last condition have similarities.
So you could put them in a single statement by combining them with an OR operator. Both return "統合".
At last, you could return directly the value instead of valuing a local variable in each matched case.
But as the second switch statement overwrites the value to return,
you should move it as the first condition to test. It would make things much clearer :
private String getKeyword(String id) {
char c = id.substring(1, 2).charAt(0);
if (c == 'W' || id.substring(4,5).charAt(0) == 'W'){
return "統合";
}
else if (c == 'E'){
return "英語";
}
else if (c == 'M'){
return "数学";
}
return "";
}
private String getKeyword(String id) {
String keyword = "";
switch (id.substring(1, 2)) {
case "E":
keyword = "英語";
break;
case "M":
keyword = "数学";
break;
case "W":
keyword = "統合";
break;
}
if ("W".equals(id.substring(4, 5))) keyword = "統合";
return keyword;
}
You could use an Enumeration.
Each of them would contain one or multiple id and the corresponding keyword: easier to read and maintain.
Finally, declare a static method in the Enumeration to retrieve the expected element depending on a provided id.
You can make a keyword map and use it instead. As this is static, you can make this map an instance variable and just use it when that method is called.
//do this in something like a constructor
Map<String, String> keywordMap = new HashMap<>;
keywordMap.put("E", "英語");
//more put calls
//and in your method, you'll only need
return keywordMap.get(id.substring(1, 2));
Pay attention to the logic implemented using two switch. As matches in the first switch don't cause the method to return, matches in the second switch can overwrite the keyword variable.
Use a map:
private static final Map<String, String> letterToKeyword =
Map.of("E", "英語", "M", "数学", "W", "統合");
private String getKeyword(String id) {
if (id.substring(4,5).equals("W")) return "統合";
return letterToKeyword.getOrDefault(id.substring(1, 2), "");
}
Prior to Java 9 building the map would be a little more verbose (but could be assisted with a helper method, if so desired):
private static final Map<String, String> letterToKeyword;
static {
Map<String, String> map = new HashMap<>();
map.put("E", "英語");
map.put("M", "数学");
map.put("W", "統合");
letterToKeyword = Collections.unmodifiableMap(map);
}
You could move the body of the switches in separately functions.
you could also remove the break's and return instantly in the cases, if you want it shorter.
You could also use enums
One of your switches can be replaced by a simple if statement.
private String getKeyword(String id)
{
if(id.substring(4,5).equals("W"))
{
return "統合";
}
else
{
switch (id.substring(1, 2)) {
case "E":
return "英語";
case "M":
return "数学";
case "W":
return "統合";
}
}
return "";
}

How to determine different behaviors with 2 booleans?

I have 2 boolean variables, for example:
boolean isWhite;
boolean isTall;
So I want to determine a different behavior for the 4 possible cases ({(isWhite, isTall), (!isWhite, isTall), (isWhite, !isTall), (!isWhite, !isTall)}).
1) Is there a more elegant and practical way to do it than with using if/else each time?
if(isWhite) {
if(isTall) {
// Case 1
}
else {
// Case 2
}
else {
if(isTall) {
// Case 3
}
else {
// Case 4
}
2) Would it be of any difference doing something like this instead?
if (isWhite && isTall)
// Case 1
if (isWhite && !isTall)
// Case 2
if (!isWhite && isTall)
// Case 3
if (!isWhite && !isTall)
// Case 4
Your second solution is more readable. But you should use if-else instead of just if-statements. Consider if the first case is already true. The other 3 if statements still will be computed. If you use if-else statements and the first case is true, the other 3 statements will be just skipped.
if (isWhite && isTall) {//case1}
else if (isWhite && !isTall) {//case2}
else if (!isWhite && isTall) {//case3}
else {//case4}
I would prefer the first if/else arrangement unless you can use an enum Say your booleans were a and b
enum Combination {
A_B, NA_B, A_NB, NA_NB;
}
where NA_NB is not A, not B.
switch(comb) {
case A_B:
//
break;
// more cases
case NA_NB:
//
break;
}
This scales a bit better when you have more boolean and it can also make combinations which are impossible clearer.
This lends itself to removing the if/else block entirely.
enum Combination implements Actionable {
A_B {
public void action(Arg arg) {
// something
}
},
NA_B {
public void action(Arg arg) {
// something
}
},
A_NB {
public void action(Arg arg) {
// something
}
},
NA_NB {
public void action(Arg arg) {
// something
}
};
}
Now instead of an if/else or switch you can just call
actionable.action(something);
You can also easily add combinations, possibly custom Actionable which were not part of the original library.
Another alternatve would be to convert them into ints (i.e. 0 and 1) and use switch statement to identify all the scenarios, e.g.:
public static void main(String[] args) throws Exception{
boolean white = true, tall = false;
StringBuilder result = new StringBuilder();
result.append(white ? 1 : 0);
result.append(tall ? 1 : 0);
switch(result.toString()){
case "00":
//do something
break;
case "01":
//do something
break;
case "10":
//do something
break;
case "11":
//do something
break;
}
}
This is an often seen pattern where properties define different cases, by some business logic (founded in the real world).
That kind of control flow is hard to test, to verify, to track bugs from.
The best way is to make it more or less a declarative list.
Maybe remotely something like:
{ false, false, (isWhite, isTall) -> { ... } },
{ false, true, (isWhite, isTall) -> { ... } },
{ true, false, (isWhite, isTall) -> { ... } },
{ true, true, (isWhite, isTall) -> { ... } },
(More readable to use enums.)
In this way you can create plugins (XML with cases), log better (handler class name). Especially this declarative list can serve as specification of business logic, readable by the customer. Therefore it would be fine if the picked handler is given a string ID+version that appears to the user in some form. For instance a form-version in a PDF.
It means change of code design. But helped me for instance in the case of many similar reports.
Yet another possibility is to calculate your caseNumber directly from the booleans :
int caseNumber = 1 + (isWhite ? 0 : 2) + (isTall ? 0 : 1);
Here's an example :
import java.util.*;
import java.lang.*;
import java.io.*;
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
System.out.println(getCaseNumber(true,true));
System.out.println(getCaseNumber(true,false));
System.out.println(getCaseNumber(false,true));
System.out.println(getCaseNumber(false,false));
switch(getCaseNumber(true,false)){
case 1:
System.out.println("Case 1!");
break;
case 2:
System.out.println("Case 2!");
break;
case 3:
System.out.println("Case 3!");
break;
case 4:
System.out.println("Case 4!");
break;
}
}
private static int getCaseNumber(boolean bool1, boolean bool2)
{
return 1 + (bool1 ? 0 : 2) + (bool2 ? 0 : 1);
}
}
It outputs :
1
2
3
4
Case 2!
It's easier to iterate over all possibilities, it scales better with n booleans and it's easier to use with a switchstatement.
1 + is just here to match your case definition, in which (true,true) is Case 1.
You can remove it if you want caseNumber between 0 and 2**n-1.
How about this? Similar to the previous answers, but I use else if to shorten my code when handling the two instances in which the booleans don't have the same value.
if(isWhite && isTall){/**case 1*/}
else if(isWhite){/**case 2*/} // only need one boolean
else if(isTall){/**case 3*/} // on each of these lines
else{/**case 4*/}
Saves you a little typing :)

Efficiently Check Multiple Conditions [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I've got a situation in which I need to check multiple conditions, where every combination has a different outcome. In my specific condition, I've got 2 variables, which are enum types, that can each be 2 different values.
enum Enum1
{
COND_1,
COND_2
}
enum EnumA
{
COND_A,
COND_B
}
Enum1 var1;
EnumA varA;
This gives me 4 possible conditions, which requires 4 different outcomes. I've come up with a few different ways of doing this, either using if statements or switch statements:
if(var1 == Enum1.COND_1 && varA == EnumA.COND_A)
{
// Code
}
else if(var1 == Enum1.COND_1 && varA == EnumA.COND_B)
{
// Code
}
else if(var1 == Enum1.COND_2 && varA == EnumA.COND_A)
{
// Code
}
else if(var1 == Enum1.COND_2 && varA == EnumA.COND_B)
{
// Code
}
Or:
switch(var1)
{
case COND_1:
switch(varA)
{
case COND_A:
// Code
break;
case COND_B:
// Code
break;
}
break;
case COND_2:
switch(varA)
{
case COND_A:
// Code
break;
case COND_B:
// Code
break;
}
break;
}
I've thought of others, but don't want to fill this up with code :P I'd like to know what the best way to do this is. I think the switch is a bit easier to read, but the ifs are shorter. I think it'd be really cool if switches could have multiple conditions, but I haven't heard of it. This also begs the question: what's the best way to do this with an arbitrary number of variables and possible values?
For your small use case I would probably go for nested if statements. But if you have plenty of enum constants, perhaps a pattern using streams could make your code easier to read and maintain (for a small performance penalty). You could solve it using a stream like this:
Stream.of(new Conditional(COND_1, COND_A, () -> {/* do something */}),
new Conditional(COND_1, COND_B, () -> {/* do something */}),
new Conditional(COND_2, COND_A, () -> {/* do something */}),
new Conditional(COND_2, COND_B, () -> {/* do something */}))
.filter(x -> x.test(var1, varA))
.findAny()
.ifPresent(Conditional::run);
That would require a supporting class:
class Conditional implements BiPredicate<Enum1, EnumA>, Runnable
{
private final Enum1 var1;
private final EnumA varA;
private final Runnable runnable;
public Conditional(Enum1 var1, EnumA varA, Runnable runnable) {
this.var1 = var1;
this.varA = varA;
this.runnable = runnable;
}
#Override
public boolean test(Enum1 enum1, EnumA enumA) {
return var1 == enum1 && varA == enumA;
}
#Override
public void run() {
runnable.run();
}
}
Performance differences are probably negligible here, so I would focus on shortness and readability. So I would just simplify the if's a bit by using temporary variables:
boolean is_1 = (var1 == Enum1.COND_1);
boolean is_A = (varA == EnumA.COND_A);
if(is_1 && is_A)
{
// Code
}
else if(is_1 && !is_A)
{
// Code
}
else if(!is_1 && is_A)
{
// Code
}
else if(!is_1 && !is_A)
{
// Code
}
I prefer the if variant without nesting, since it is short and you have all the conditions in one line.
When stopping through the code during debugging, it can get tedious though, since you have to step over all preceding conditions, which is O(n). When executing the code, this shouldn't matter since the compiler will probably optimize the code.
There is no obvious best way, so you will have to experiment a bit.
I definitely prefer the flat version, it could just use a little less duplication:
// If you can't make the variables final, make some final copies
final Enum1 var1 = Enum1.COND_2;
final EnumA varA = EnumA.COND_B;
class Tester { // You could also make an anonymous BiPredicate<Enum1, EnumA>
boolean t(Enum1 v1, EnumA vA) {
return var1 == v1 && varA == vA;
}
};
Tester tes = new Tester();
if (tes.t(Enum1.COND_1, EnumA.COND_A)) {
// code
} else if (tes.t(Enum1.COND_1, EnumA.COND_B)) {
// code
} else if (tes.t(Enum1.COND_2, EnumA.COND_A)) {
// code
} else if (tes.t(Enum1.COND_2, EnumA.COND_B)) {
// code
}
Run it here. You could maybe make it even shorter and less redundant by doing a static import of the enums to avoid mentioning the enum names, e.g. tes.t(COND_1, COND_B). Or if you're willing to give up some compile time safety you can pass a string which gets converted to the two enum values, e.g. tes.t("COND_1 COND_A") (the implementation is left to the reader).
Maybe crazy idea but you could construct an int or a byte using the flags and use it in a single switch.
private int getIntegerStateForConditions(boolean... conditions ){
int state = 0;
int position = 0;
for(boolean condition: conditions){
if(condition){
state = state || (1 << position++);
}
}
return state;
}
...
switch(getIntegerStateForCondition((var1 == Enum1.COND_1), (var2 == EnumA.COND_A)){
case 0: ... //both condition false
case 1: ... //first condition true second false
case 2: ... //first false, second true ...
}
...
I think this is very far from being clean code but it looks better.
If I were you I would rely on bit flags in order to have only one byte (as you have only 4 use cases) to deal with and use a switch statement on this byte to manage all your use cases.
Something like this:
private static final int COND_2 = 1;
private static final int COND_B = 2;
private byte value;
public void setValue(Enum1 enum1) {
if (enum1 == Enum1.COND_1) {
this.value &= ~COND_2;
} else {
this.value |= COND_2;
}
}
public void setValue(EnumA enumA) {
if (enumA == EnumA.COND_A) {
this.value &= ~COND_B;
} else {
this.value |= COND_B;
}
}
public Enum1 getEnum1() {
return (this.value & COND_2) == COND_2 ? Enum1.COND_2 : Enum1.COND_1;
}
public EnumA getEnumA() {
return (this.value & COND_B) == COND_B ? EnumA.COND_B : EnumA.COND_A;
}
Then your tests would be:
switch (value) {
case 0 :
// 1-A;
break;
case 1 :
// 2-A;
break;
case 2 :
// 1-B;
break;
case 3 :
// 2-B;
break;
}
I would personally prefer this:
if(understandableNameInContextName1(var1, varA))
{
// Code
}
else if(understandableNameInContextName2(var1, varA))
{
// Code
}
else if(understandableNameInContextName3(var1, varA))
{
// Code
}
else if(understandableNameInContextName4(var1, varA))
{
// Code
}
private boolean understandableNameInContextName1(Object var1, Object varA){
return (var1 == Enum1.COND_1 && varA == EnumA.COND_A);
}
private boolean understandableNameInContextName2(Object var1, Object varA){
return (var1 == Enum1.COND_1 && varA == EnumA.COND_B);
}
private boolean understandableNameInContextName3(Object var1, Object varA){
return (var1 == Enum1.COND_2 && varA == EnumA.COND_A);
}
private boolean understandableNameInContextName4(Object var1, Object varA){
return (var1 == Enum1.COND_2 && varA == EnumA.COND_B);
}
And the names of the methods could be like, isOrderShippedAndDelivered(), isRequestSendAndAckRecieved().
The reason is that this is going to make the code a lot more readable.
Unless you have data that leads you back to these if statement there is not going to be much gain optimizing these.
See:
https://softwareengineering.stackexchange.com/questions/80084/is-premature-optimization-really-the-root-of-all-evil
Kind of depends on the complexity of the code and number of combinations but another option is a dictionary with the key comprising a Tuple of your enumerations and a value of a delegate to the code.

I don´t know how to push a tree into a Stack

Hi I am working with the Stack class in java, the problem that I have is that in this Stack I want to insert (to push) elements of type String, but i also want to insert a tree, the code is the following:
public static void Expression(Stack<String> exp)
{
boolean error = false;
String leftExp,rightExp = "";
Stack<String> stackOp = new Stack<String>();
while(!exp.empty() && (error == false))
{
switch(elementType(exp.peek())){
case 'I':
error = true;
break;
case 'O':
if(stackOp.size() < 2)
error = true;
else
{
rightExp = stackOp.pop();
leftExp = stackOp.pop();
Tree subTree = new Tree();
subTree.insertNode(exp.peek());
subTree.insertNode(rightExp);
subTree.insertNode(leftExp);
stackOp.push(subTree);//here is were I have the mistake
}
break;
default:
stackOp.push(exp.peek());
}
}
}
public static char elementType(String car){
char c = 'Z';
if(car.equals("("))
c = 'I';
else if(car.equals(")"))
c = 'D';
else if(car.equals("+") || car.equals("-") || car.equals("*") || car.equals("/"))
c = 'O';
return c;
}
This code basically transforms a math expression into a binary tree, for this I need an input, which is the expression, an output which is the binary tree, and another local stack that contains variables, numbers, and subtrees. But how can i make a Stack that contains elements of different types?
Create a class that can hold anything you want to put on the stack -- I think using a string to designate your operation is a bit clumsy, but suit yourself. If you have a class that is StackElement, it can contain a type indicator (look into Java enums) and methods to do or obtain whatever you want.
You could define StackElement to contain a reference to one of several types, then also define all the methods of all the types it might contain; the ones that apply would be pass-throughs (if the type is an operation, a pass-through for getOperationType()), and the others would throw illegalOperationException, or something. So if you try to call getOperationType() on a value, it throws an exception, same for calling getValue() on an operation, etc.
A nice thing about doing it this way is that you do not have to do any instanceof testing of the types you have stored. You can declare your FILO queue to hold StackElement objects, create them with the types you want, and use them, all without instanceof or otherwise breaking OO style.
public class StackElement
{
private StackElementType type;
private StackOperation operation;
private StackValue value;
public StackElementType getType() { return type; }
public StackOperation getOperation()
{
switch (type)
{
case StackElementType.OPERATION: return operation;
default: throw IllegalOperationException
("getOperation() on type " + type.toString());
}
}
public StackValue getValue()
{
switch (type)
{
case StackElementType.VALUE: return value;
default: throw IllegalOperationException
("getValue on type " + type.toString());
}
}
}

Categories