Java switch case statement issue - java

I'm trying to use a switch statement in Android aplication,where I have to check if an integer is equal to some of the Enum's value.The code goes like this :
public enum RPCServerResponseCode{
E_INCORRECT_LOGIN(4001),
E_ACCOUNT_DISABLED(4002),
E_ACCOUNT_NOT_ACTIVE(4003);
private int value;
private RPCServerResponseCode(int i) {
this.value=i;
}
public static RPCServerResponseCode getByValue(int i) {
for(RPCServerResponseCode dt : RPCServerResponseCode.values()) {
if(dt.value == i) {
return dt;
}
}
throw new IllegalArgumentException("No datatype with " + i + " exists");
}
}
}
And my switch statement looks like this :
int errorCode;
switch(errorCode){
case RPCServerResponseCode.E_INCORRECT_LOGIN :
{
if (user.isAuthenticated)
{
// logout before login
}
break;
}
case RPCServerResponseCode.E_ACCOUNT_NOT_ACTIVE:
{
if (user.isAuthenticated)
{
//logout
}
break;
}
}
}
But I get error saying this : "Type mismatch: cannot convert from RPCCommucatorDefines.RPCServerResponseCode to int".
Any suggestions how to solce that issue? Thanks in advance!!!

errorcode is int. Should be of type RPCServerResponseCode, so you could use something like:
switch (RCPServerResponseCode.getByValue(errorcode))
{
...
}

You're trying to compare your INT error code to a RPCServerResponseCode instance - This isn't possible. You need to use the method getByValue in your RPCServerResponseCode class to do the conversion for you. After that, you can use the result (Which will be a RPCServerResponseCode instance) in your switch statement:
int errorCode;
RPCServerResponseCode responseCode = RPCServerResponseCode.getByValue(errorCode);
switch(responseCode){
case RPCServerResponseCode.E_INCORRECT_LOGIN :
{
if (user.isAuthenticated)
{
// logout before login
}
break;
}
case RPCServerResponseCode.E_ACCOUNT_NOT_ACTIVE:
{
if (user.isAuthenticated)
{
//logout
}
break;
}
}
}

Java enums are fully-fledged objects and cannot be implicitly cast to integers.
This should work:
switch(RPCServerResponseCode.getByValue(errorCode)){

you can say
int errorCode=4001;
RPCServerResponseCode code = RPCServerResponseCode.getByValue(errorCode);
switch(code){
...
}

Related

How to compare a string with an Enum in a switch?

i have an enum like below:
public enum TripType {
TRIP1,
TRIP2,
TRIP3,
TRIP4
}
Then i have a method which receives a string parameter setupTravel(String tripType) and i need to check the value of tripType with a TripType. I wish to use a switch-case as below:
setupTravel(String tripType){
switch (tripType){
case TripType.TRIP1.toString():
setup1();
break;
case TripType.TRIP2.toString():
setup2();
break;
}
}
But, in the line TripType.TRIP1.toString() it complains with:
Constant expression required
How can i fix it?
setupTravel(String tripType) {
try {
switch (TripType.valueOf(tripType)) {
case TRIP1:
setup1();
break;
case TRIP2:
setup2();
break;
}
} catch (IllegalArgumentException ex) {
// Handle invalid trip type here
}
}
Since your method seems to switch on the Enum string, why not just use the Enum? You may want to include a default unless you have all Enums in the switch block.
setupTravel(TripType type){
switch (type){
case TRIP1:
setup1();
break;
case TRIP2:
setup2();
break;
}
}
You would call it as
setupTravel(TripType.TRIP1)
If it will be useful for you, You can use an enum constructor instead of using a switch case.
Something like this:
public enum TripType {
TRIP1("setup1"), TRIP2("setup2");
String setup;
private TripType(String setup) {
this.setup = setup;
}
public String getSetup() {
return setup;
}
public static TripType getTripTypeByName(String name) {
return Arrays.stream(TripType.values()).filter(t -> t.name().equalsIgnoreCase(name)).findAny().orElse(null);
}
}
And getting enum based on the name, you can use like this:
TripType tripType = TripType.getTripTypeByName("trip1");
System.out.println(tripType != null ? tripType.getSetup() : null);

Make code cleaner by making a method that can accept an enum

So currently, I have some code, that is extremely messy and does the opposite of following dry rules. This is killing me internally, and I would love to fix it, if I knew how.
I have a class called Commands, this have two enums in it. Subs, and Options. What I would like to do, is make a method, such as this one here:
public void makeTab(String args, List<String> command, Commands type) {
if (args.equals("")) {
for (Commands.type commd : Commands.type.values()) {
command.add(commd.name().toLowerCase());
}
} else {
for (Commands.type commd : Commands.type.values()) {
if (commd.name().toLowerCase().startsWith(args)) {
command.add(commd.name().toLowerCase());
}
}
}
}
Then, if that method actually worked like I wanted it to, I could then do this.
List<String> command = new ArrayList<>();
switch (args.length) {
case 1:
makeTab(args[0], command, Subs);
break;
case 2:
makeTab(args[1], command, Options);
break;
}
Very sadly though, as you should be able to tell, this doesn't work, specifically because of the "Commands type" bit in the method. The problem is, I don't know what to put there to work, I've tried "Class type", "enum type", "Enum type". So, because of this catastrophe, my code is currently looking like this.
List<String> comd = new ArrayList<>();
switch (args.length) {
case 1:
if (args[0].equals("")) {
for (Commands.Subs commd : Commands.Subs.values()) {
comd.add(commd.name().toLowerCase());
}
} else {
for (Commands.Subs commd : Commands.Subs.values()) {
if (commd.name().toLowerCase().startsWith(args[0])) {
comd.add(commd.name().toLowerCase());
}
}
}
break;
case 2:
if (args[1].equals("")) {
for (Commands.Options commd : Commands.Options.values()) {
comd.add(commd.name().toLowerCase());
}
} else {
for (Commands.Options commd : Commands.Options.values()) {
if (commd.name().toLowerCase().startsWith(args[1])) {
comd.add(commd.name().toLowerCase());
}
}
}
break;
}
tl;dr I'm trying to make the last code block cleaner by making a method for the if, else.
Rather than taking values yourself, have the caller pass values() to you:
public void makeTab(String args, List<String> command, Enum[] values) {
for (Enum commd : values) {
String lowerName = commd.name().toLowerCase()
if (lowerName.startsWith(args)) {
command.add(lowerName);
}
}
}
The caller would invoke your method as follows:
makeTab(args, command, Options.values());
makeTab(args, command, Subs.values());
Note that there is no need to check args to be an empty string, because when args is empty, startsWith(args) returns true for any String value.

How to change a lot of if/else where class should be returned in Java

My eyes are bleed when I see a method with a lot of if/else statements in one method.
And I'd like to fix that somehow.
Method is:
private Class getMapType(String header) {
if (header.equals(Bean01.getHeader())) {
return Bean01.class;
} else if (header.equals(Bean02.getHeader())) {
return Bean02.class;
} else if (header.equals(Bean03.getHeader())) {
return Bean03.class;
} else if (header.equals(Bean04.getHeader())) {
return Bean04.class;
} else
logger.log(Logger.FATAL, "File does not exist");
return null;
}
getHeader() method is different for each class.
So, how can I rework it?
One way is to use static final Map
private static final Map<String, Class> TYPE_MAPPING = new ImmutableMap.Builder()
.put("Bean01", Bean01.class)
.put("Bean02", Bean02.class)
.put("Bean03", Bean03.class)
.build();
private Class getMapType(String header) {
return TYPE_MAPPING.get(header);
}
Another way is to use reflections:
private Class getMapType(String header) {
for (int i = 1; i <= 4; i++)
if (header.equals(Class.forName("com.mycompany.Bean0"+i).getMethod( "getHeader").invoke(null)))
return Class.forName("com.mycompany.Bean0"+i);
logger.log(Logger.FATAL, "File does not exist");
return null;
}
You have to change class name in forName method (it must be fully qualified).
Try using switch (Java 1.7 onwards Switch works with String):
private Class getMapType(String header) {
switch(header):
case "Bean01":
return Bean01.class;
case "Bean02":
return Bean02.class;
case "Bean03":
return Bean03.class;
case "Bean04":
return Bean04.class;
default:
logger.log(Logger.FATAL, "File does not exist");
return null;
}

Need design suggestions for nested conditions

I need to write the logic with many conditions(up to 30 conditions) in one set of rule with many if else conditions and it could end in between or after all the conditions.
Here is the sample code I have tried with some possible scenario. This gives me result but doesn't look good and any minor miss in one condition would take forever to track.
What I have tried so far is, Take out common conditions and refactored to some methods. Tried creating interface with conditions and various set would implement it.
If you have any suggestion to design this, would help me. Not looking for detailed solution but even a hint would be great.
private Boolean RunCondition(Input input) {
Boolean ret=false;
//First if
if(input.a.equals("v1")){
//Somelogic1();
//Second if
if(input.b.equals("v2"))
//Third if
if(input.c >1)
//Fourth if
//Somelogic2();
//Go fetch key Z1 from database and see if d matches.
if(input.d.equals("Z1"))
System.out.println("Passed 1");
// Fourth Else
else{
System.out.println("Failed at fourth");
}
//Third Else
else{
if(input.aa.equals("v2"))
System.out.println("Failed at third");
}
//Second Else
else{
if(input.bb.equals("v2"))
System.out.println("Failed at second");
}
}
//First Else
else{
if(input.cc.equals("v2"))
System.out.println("Failed aat first");
}
return ret;
}
public class Input {
String a;
String b;
int c;
String d;
String e;
String aa;
String bb;
String cc;
String dd;
String ee;
}
The flow is complicated because you have a normal flow, plus many possible exception flows when some of the values are exceptional (e.g. invalid).
This is a perfect candidate to be handled using a try/catch/finally block.
Your program can be rewritten into following:
private Boolean RunCondition(Input input) {
Boolean ret=false;
try {
//First if
if(!input.a.equals("v1")) {
throw new ValidationException("Failed aat first");
}
//Somelogic1();
//Second if
if(!input.b.equals("v2")) {
throw new ValidationException("Failed at second");
}
//Somelogic2()
//Third if
if(input.c<=1) {
throw new ValidationException("Failed at third");
}
//Fourth if
//Somelogic2();
//Go fetch key Z1 from database and see if d matches.
if(!input.d.equals("Z1")) {
throw new ValidationException("Failed at fourth");
}
System.out.println("Passed 1");
} catch (ValidationException e) {
System.out.println(e.getMessage());
}
return ret;
}
Where you can define your own ValidationException (like below), or you can reuse some of the existing standard exception such as RuntimeException
class ValidationException extends RuntimeException {
public ValidationException(String arg0) {
super(arg0);
// TODO Auto-generated constructor stub
}
/**
*
*/
private static final long serialVersionUID = 1L;
}
You can read more about this in
https://docs.oracle.com/javase/tutorial/essential/exceptions/index.html
Make a separate class for the condition:
package com.foo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class App
{
static class Condition<T> {
final int idx;
final T compareValue;
public Condition(final int idx, final T compareValue) {
this.idx = idx;
this.compareValue = compareValue;
}
boolean satisfies(final T other) {
return other.equals(compareValue);
}
int getIdx() {
return idx;
}
}
public static void main( String[] args )
{
final List<Condition<String>> conditions = new ArrayList<Condition<String>>();
conditions.add(new Condition<String>(1, "v1"));
conditions.add(new Condition<String>(2, "v2"));
final List<String> inputs = new ArrayList<String>(Arrays.asList("v1", "xyz"));
boolean ret = true;
for (int i = 0; i < inputs.size(); i++) {
if (!conditions.get(i).satisfies(inputs.get(i)))
{
System.out.println("failed at " + conditions.get(i).getIdx());
ret = false;
break;
}
}
System.out.println("ret=" + ret);
}
}
#leeyuiwah's answer has a clear structure of the conditional logic, but exceptions aren't the right tool for the job here.
You shouldn't use exceptions to cope with non-exceptional conditions. For one thing, exceptions are really expensive to construct, because you have to walk all the way up the call stack to construct the stack trace; but you don't need the stack trace at all.
Check out Effective Java 2nd Ed Item 57: "Use exceptions only for exceptional conditions" for a detailed discussion of why you shouldn't use exceptions like this.
A simpler option is to define a little helper method:
private static boolean printAndReturnFalse(String message) {
System.out.println(message);
return false;
}
Then:
if(!input.a.equals("v1")) {
return printAndReturnFalse("Failed aat first");
}
// etc.
which I think is a simpler; and it'll be a lot faster.
Think of each rule check as an object, or as a Strategy that returns whether or not the rule passes. Each check should implement the same IRuleCheck interface and return a RuleCheckResult, which indicates if the check passed or the reason for failure.
public interface IRuleCheck
{
public RuleCheckResult Check(Input input);
public String Name();
}
public class RuleCheckResult
{
private String _errorMessage;
public RuleCheckResult(){}//All Good
public RuleCheckResult(String errorMessage)
{
_errorMessage = errorMessage;
}
public string ErrorMessage()
{
return _errorMessage;
}
public Boolean Passed()
{
return _errorMessage == null || _errorMessage.isEmpty();
}
}
public class CheckOne implements IRuleCheck
{
public RuleCheckResult Check(Input input)
{
if (input.d.equals("Z1"))
{
return new RuleCheckResult();//passed
}
return new RuleCheckResult("d did not equal z1");
}
public String Name();
}
Then you can simply build a list of rules and loop through them,
and either jump out when one fails, or compile a list of failures.
for (IRuleCheck check : checkList)
{
System.out.println("checking: " + check.Name());
RuleCheckResult result = check.Check(input);
if(!result.Passed())
{
System.out.println("FAILED: " + check.Name()+ " - " + result.ErrorMessage());
//either jump out and return result or add it to failure list to return later.
}
}
And the advantage of using the interface is that the checks can be as complicated or simple as necessary, and you can create arbitrary lists for checking any combination of rules in any order.

Ensure every enum value is used

If I´m using an enum to determine the type of a task.
public enum TaskType {
TYPE_ONE("Type1"),TYPE_TWO("Type2"),TYPE_THREE("Type3");
private final String type;
private StageType(String type) {
this.type = type;
}
#Override
public String toString() {
return type;
}
}
how can I assure at one point in my Application
if(taskType == TaskType.TYPE_ONE) {
typeOneProcessing();
} else if(taskType == TaskType.TYPE_TWO) {
typeTwoProcessing();
} else if(taskType == TaskType.TYPE_THREE) {
typeThreeProcessing();
}
that every enum value is used?
I mean if I need to add a new TYPE_FOUR someday, I´d need to find every place in my code where I used the enum, so I ask myself if there is a better way so that I either avoid the enum and use some other concept or that I can ensure that every value of the enum is used in that piece of code.
There are findbugs type tools for doing that but you could consider removing the if-then-else completely and put the processing inside the enum. Here, adding a new TYPE_FOUR will force you to write it's doProcessing() method.
public interface DoesProcessing {
public void doProcessing();
}
public enum TaskType implements DoesProcessing {
TYPE_ONE("Type1") {
#Override
public void doProcessing() {
}
},
TYPE_TWO("Type2") {
#Override
public void doProcessing() {
}
},
TYPE_THREE("Type3") {
#Override
public void doProcessing() {
}
},
TYPE_FOUR("Type4") {
// error: <anonymous com.oldcurmudgeon.test.Test$TaskType$4> is not abstract and does not override abstract method doProcessing() in DoesProcessing
};
private final String type;
private TaskType(String type) {
this.type = type;
}
#Override
public String toString() {
return type;
}
}
public void test() {
DoesProcessing type = TaskType.TYPE_TWO;
type.doProcessing();
}
If you would prefer an abstract method then this works:
public enum TaskType {
TYPE_ONE("Type1") {
#Override
public void doProcessing() {
}
},
TYPE_TWO("Type2") {
#Override
public void doProcessing() {
}
},
TYPE_THREE("Type3") {
#Override
public void doProcessing() {
}
};
private final String type;
private TaskType(String type) {
this.type = type;
}
// Force them all to implement doProcessing.
public abstract void doProcessing();
#Override
public String toString() {
return type;
}
}
You could put the process method as an abstract method in TaskType, and then override it in every task in the enum. What would probably be a better idea is if you create an interface, something like:
public interface Task {
void process();
}
Then you either let your enum implement this interface. Or, probably better, you create concrete classes implementing this interface. One class for each of your task types.
I think you are saying that you are wanting the compiler to tell you that all of the enum's values are considered.
Unfortunately, Java doesn't support that.
You might think that you could write something like this:
public int method(TaskType t) {
switch (t) {
case TYPE_ONE: return 1;
case TYPE_TWO: return 2;
case TYPE_THREE: return 3;
}
// not reachable ... no return required
}
... and rely on the compiler to tell you if you left out one of the enum values in the switch cases.
Unfortunately, it doesn't work!! The above is a compilation error anyway. According to the JLS reachability rules, the switch statement needs a default: arm for that method to be valid. (Or you can add a return at the end ...)
There is a good reason for this oddity. The JLS binary compatibility rules say that adding a new value to an enum is a binary compatible change. That means that any code with switch statement that switches on an enum needs to still remain valid (executable) code after the addition of enum values. If method was valid to start with, it can't become invalid (because there is a return path with no return statement) after the binary compatible change.
In fact, this is how I would write the code above:
public int method(TaskType t) {
switch (t) {
case TYPE_ONE: return 1;
case TYPE_TWO: return 2;
case TYPE_THREE: return 3;
default:
throw new AssertionError("TaskType " + t + " not implemented");
}
// not reachable ... no return required
}
This doesn't pretend to be compile-time safe, but it is fail-fast, and it doesn't involve bad OO design.
AFAIK you can't do it "automatically".
To minimize the risk of forgetting to add an if/case for new value you could have one "service" class for each enum value and a factory which provides a specific service for enum value.
E.g. instead of:
void methodA(TaskType type) {
doSth();
switch(type) {
case TYPE_ONE:
foo1();
break;
case TYPE_TWO:
foo2();
break;
...
}
}
void methodB(TaskType type) {
doSthElse();
switch(type) {
case TYPE_ONE:
bar1();
break;
case TYPE_TWO:
bar2();
break;
...
}
}
do:
interface Service {
foo();
bar();
}
class ServiceFactory {
Service getInstance(TaskType type) {
switch(type) {
case TYPE_ONE:
return new TypeOneService();
case TYPE_TWO:
return new TypeTwoService();
default:
throw new IllegalArgumentException("Unsupported TaskType: " + type);
}
}
}
And then the methods above can be rewritten as follows:
void methodX(TaskType type) {
doSth();
ServiceFactory.getInstance(type).foo();
}
This way you have only one point where you have to add handling of new enum value.
HashMap<String, Integer> hm=new HashMap<String, Integer>();
...
if(taskType == TaskType.TYPE_ONE) {
typeOneProcessing();
hm.put(TaskType.TYPE_ONE, 1)
} else if(taskType == TaskType.TYPE_TWO) {
typeTwoProcessing();
hm.put(TaskType.TYPE_TWO, 1)
} else if(taskType == TaskType.TYPE_THREE) {
typeThreeProcessing();
hm.put(TaskType.TYPE_THREE, 1)
}
...
for (TaskType t : TaskType.values()) {
if(hm.get(t)!=1)
// Trigger the alarm
}
You can even count the times the element was count if you need it
You can do swich case on the enum, and fail if the default is hit:
switch(taskType ){
case TYPE_ONE: ... break;
case TYPE_TWO: ... break;
case TYPE_THREE: ... break;
default:
throw new IllegalStateException("Unsupported task type:"+taskType);
}

Categories