Java - Alternative to very long switch case - java

In my Java "Pear" class, I have a huge list of approximately 1000 variables :
public class Pear {
private String
a100,
a110,
a120,
...
etc.
}
I need to set each one of these variables based on the given value of a banana, so I had a first basic idea to do it with a switch case :
public class Pear {
...
public void setValues(Banana aBanana) {
switch (aBanana.getValueName()) {
case "300886":
a100 = aBanana.getValue();
break;
case "309606":
a110 = aBanana.getValue();
break;
case "300843":
a120 = aBanana.getValue();
break;
/* ...and so on for 1000 variables*/
}
}
}
, but I feel like this is not the good way to accomplish this, and this is not going to be very readable neither maintainable. How could I replace this switch case ?
Edit : I think there is a misunderstanding on the call of "setValues". It is going to be called like this (I added some pears) :
public static void main(String[] bananas) {
Pear pear = new Pear();
pear.setValues(bananas[0]);
pear.setValues(bananas[1]);
pear.setValues(bananas[2]);
...etc for 200 times approximately...
}

Having hundreds of variables to store multiple values of the same kind is room for bugs and difficult maintenance (which led to this question).
If you changed your data structure, you would get rid of all the unnecessary variable declarations, and you would have logic coded against values (codes), rather than variable names.
A Map is designed to be used to associate keys to values (value names to values, in your case).
Map<String, String> valueMap = new HashMap<>();
public void setValues(Banana aBanana) {
valueMap.put(aBanana.getValueName(), aBanana.getValue());
}
Now this introduces changes elsewhere, but then that's justified because your typical "read" code would start from the same "value names" too:
public String getValue(String valueName) {
return this.valueMap.get(valueName);
}

Related

Working with constants in Java: How to log the name given to them?

Let's say we have one class that looks like this:
public class UserAction {
static final int ACTION_USER_WANTS_TO_DO_A = 1001;
static final int ACTION_USER_WANTS_TO_DO_B = 1002;
// ...
// 'sub-grouping'
static final int[] ALL_ACTIONS_ALLOWED_IN_STATE_X = {
ACTION_USER_WANTS_TO_DO_A,
ACTION_USER_WANTS_TO_DO_D,
ACTION_USER_WANTS_TO_DO_Q,
// ...
}
}
... and another class that looks like this:
public class Model {
public void onActionableEvent(int action) {
// check for state mismatch by iterating over sub-groups
// if (fail) {return;}, if pass:
Log.i("XXX","processing: " + action); // <----- this is the problem.
switch (action) {
case: .ACTION_USER_WANTS_TO_DO_A: {
//
break;
}
case: .ACTION_USER_WANTS_TO_DO_B: {
//
break;
}
}
}
}
I'm having trouble logging the actual name of the actions instead of the raw int... without doing whole bunch of inefficient code -- eg. logging raw Strings in each case block separately, using a Hashmap where refactoring the names will become cumbersome.
My question is: What data structure can be used that will:
1) Allow 'UserActions' to be sub-grouped as they are in the UserAction class -- in a way that the sub-group can be iterated over. (This rules out Enum, for example).
2) Will show the actual name of the action in the Log (.toString(), for example), instead of just showing the actual int value (number)? (seems to rule out int... which is what I'm using).
3) Can be used statically as in the example without having to construct an instance of UserAction.
I'd say that enum is what you need. Something like this:
enum USER_ACTIONS {
ACTION_USER_WANTS_TO_DO_A,
ACTION_USER_WANTS_TO_DO_B
};
And trying to answer your 3 questions:
1) they are groupped in the enum
2) in the log you'll get processing: ACTION_USER_WANTS_TO_DO_A
3) yes

Java: replace switch with lambdas. Worth it?

Using blocks of code with switch or if is a common thing when checking for events. It can be clean code when made simple, but still seems to have more lines than needed, and could be simplified using lambdas.
Block with if:
if(action == ACTION_1){
doAction1();
} else if(action == ACTION_2){
doAction2();
} else {
doDefaultAction();
}
Block with switch:
switch(action){
case ACTION_1:
doAction1();
break;
case ACTION_2:
doAction2();
break;
default:
doDefaultAction();
}
Block with lambdas using the utility class With below:
with(action)
.when(ACTION_1, this::doAction1)
.when(ACTION_2, this::doAction2)
.byDefault(this::doDefaultAction)
Using lambdas has less code, but the question is: is it easier to read than the others? Easier to maintain? Regarding performance lambdas is the worst, but for cases where performance is not important the lambdas version is shorter than the switch/if blocks.
So, how do you see it? Maybe there is a Kotlin way shorter than this, I try to focus on java only, I love Kotlin but the compilation is still too slow for my projects.
A similar utility class could be used when the block must return a specific value.
FYI, the class for the lambdas is here, I didn't check for errors, just made it quickly for this example:
public class With<T> {
private final T id;
private boolean actionFound;
private With(T id) {
this.id = id;
}
public static <T> With<T> with(T id) {
return new With<>(id);
}
public With<T> when(T expectedId, Action action) {
if (!actionFound && id == expectedId) {
actionFound = true;
action.execute();
}
return this;
}
public void byDefault(Action action) {
if (!actionFound) {
action.execute();
}
}
#FunctionalInterface
interface Action {
void execute();
}
}
As a couple has said, replacing switch with compounded methods is less efficient. Depending on your use-case, it might even be worth it to use your implementation.
Funnily enough, Oracle is actually planning to implement lambdas within switch statements, as seen in this recent JEP.
Example:
String formatted = switch (s) {
case null -> "(null)";
case "" -> "(empty)";
default -> s;
}
The switch is more flexible in that you can call functions with varying numbers of arguments, or call more than one function. You can also more easily denote when two cases lead to the same action. The fact that it's faster is just a bonus.
So in that sense I'm not sure what your With class is really adding.
However, switch has a limited number of types that it can work with. Perhaps your With class would prove to be more useful if you were to pass it predicates rather than performing simple reference equality, for example:
public With<T> when(Predicate<T> expected, Action action) {
if (!actionFound && expected.test(id)) {
actionFound = true;
action.execute();
}
return this;
}
Sample usage:
final String test = "test";
with(test)
.when(String::isEmpty, this::doAction1)
.when(s -> s.length() == 3, this::doAction2)
.byDefault(this::doDefaultAction);
replace switch with lambdas. Worth it?
No.
Because in an OO language the replacemenst for a switch or an if/else cascade is polymorphism, not "fluent API".
One option to do this is to declare static final Map<T, Action> EXPECTED_ID_TO_ACTION. Then you just can EXPECTED_ID_TO_ACTION.getOrDefault(actionId, DEFAULT_ACTION).execute(), turning ugly switch or multiple ifs into one-liner.

Enum vs If-else

I have a requirement wherein I need to build an employee object as below from an event list. Currently I've written my code as below, but QE gave a comment saying possible use of enums instead of multiple if else's.
Can someone suggest me on how to achieve this with enums.
Employee e= new Employee();
for(Event event:events){
if("empid".equals(event.getName())
e.setEmployeeId(event.getvalue());
else if("empname".equals(event.getName())
e.setEmployeeName(event.getvalue());
else if("empsal".equals(event.getName())
e.setEmployeeSal(event.getvalue());
else if("empdob".equals(event.getName())
e.setEmployeeDOB(event.getvalue());
else if("emprole".equals(event.getName())
e.setEmployeeRole(event.getvalue());
}
If you are in control of development of Event, I believe what your QE saying is to replace event name by an enum (which is a sane design as you have already decide what is the possible types of event). However, if Event's design is out of your control, or you cannot have a child class of Event for your use (e.g. make an EmployeeEvent), then just ignore what I am going to say)
i.e.
enum EventType {
EMP_ID,
EMP_NAME,
....
}
interface Event {
EventType getType(); // instead of getName() which returns a String
}
Then your code can be simplified to
Employee e= new Employee();
for (Event event: events) {
switch (event.getType()) {
case EMP_ID:
e.setEmployeeId(event.getvalue());
break;
case EMP_NAME:
e.setEmployeeName(event.getvalue());
break;
....
}
}
You may even preset the action to do against each event type, using a map (which is of similar idea of another answer)
Map<EventType, BiConsumer<Employee, String>> eventActions = new EnumMap<>();
eventActions.put(EventType.EMPLOYEE_ID, Employee::setEmployeeID);
eventActions.put(EventType.EMPLOYEE_NAME, Employee::setEmployeeName);
so you can further simplify the above switch by:
Employee e= new Employee();
for (Event event: events) {
eventActions.get(event.getType()).accept(e, event.getValue()));
}
Create an enum with the below codes
public enum EMPLOYEE_FIELDS {EMPID, EMPNAME,EMPSAL, EMPDOB, EMPROLE};
EMPLOYEE_FIELDS empField = EMPLOYEE_FIELDS.valueOf(event.getName().toUpperCase());
switch(empField ){
case EMPID:
//do something here
break;
case EMPNAME:
//do something here
break;
// other cases.
// other cases.
// other cases.
default:
//do something here
}
Hope this helps!
I would suggest you move the logic of what to do with events into the enum. If you are using Java 8 then it would look something like:
enum EmployeeField {
ID("empid", Employee::setEmployeeID),
NAME("empname", Employee::setEmployeeName),
SALARY("empsalary", Employee::setEmployeeSalary),
...
private final String key;
private final BiConsumer<Employee, String> valueSetter;
EmployeeField(String key, BiConsumer<Employee, String> valueSetter) {
this.key = key;
this.valueSetter = valueSetter;
}
public void setEmployeeField(Employee employee, String value) {
valueSetter.accept(employee, value);
}
public static EmployeeField getFieldForKey(String key) {
return Arrays.stream(values[])
.filter(ef -> ef.key.equals(key))
.findAny()
.orElseThrow(new IllegalArgumentException("No employee field " + key));
}
}
Then you can dispense with the switch statement altogether and just use:
events.stream()
.forEach(ev -> EmployeeField.getFieldForKey(ev.getName())
.setEmployeeField(emp, ev.getValue()));
This also means that all the information about employee fields, including how to set employee values, is encapsulated in the enum and can be easily changed or extended without anything else being impacted.
Note that you can do a similar thing prior to Java 8 without using the lambdas but it's not as elegant (in my view) as the anonymous interface instances need to become explicit which makes the code a lot more complicated. Or you can override methods for each enum member which (in my view) is even uglier.

How do I find the value of each Switch Case Item in C# or Java?

Say for instance I have this:
string myString = "hello";
switch (myString) {
case "hello":
break;
case "goodbye":
break;
}
Now I want a method or piece of code, that will retrieve the value of each case declared.
For example, I want to output "hello" and "goodbye" is there a way to get these values?
I was looking for something like this (HYPOTHETICAL):
foreach (case in switch(myString)){
System.out.println("")
}
//which I want output to look like this:
System.out.println("hello")
System.out.println("goodbye")
as a result of the foreach loop. Is something like this possible?
Thanks,
In C# (the answer for Java could be quite different…IMHO it's bad form to have used both tags):
There is no practical way to do what you want. You'd have to reimplement something like dotPeek's or Reflector's functionality, getting the IL for the method and decompiling it to recover the switch statement cases.
However, note that a switch statement can be implemented as a dictionary-based dispatch of delegates. If you do that, then you can just enumerate the keys of your dictionary.
For example:
Dictionary<string, Action> switchStatement = new Dictionary<string, Action>
{
{ "hello", (Action)MyHelloAction },
{ "goodbye", (Action)MyGoodbyeAction },
};
void MyHelloAction() { /* ... */ }
void MyGoodbyeAction() { /* ... */ }
Then you can simply do something like:
foreach (string switchCase in switchStatement.Keys)
{
Console.WriteLine(switchCase);
}
Actually using the switchStatement would look like:
string myString = "hello";
switchStatement[myString]();
In C#, there are 2 reasonably easy solutions to the problem , both using Dictionary-based dispatching. The first is to use a delegate (see Peter Duniho's answer for details). The second would be:
interface Dispatcher
{
void Action();
}
class HelloDispatcher:Dispatcher
{
internal void Action()
{
//place code here
}
}
class GoodbyDispatcher:Dispatcher
{
internal void Action()
{
//place code here
}
}
Then, use Dictionary<string, Dispatcher> type to hold the commands.
In Java, you can only use the second solution; I believe you would want a TreeMap there, although I'm not completely sure.
You can do it without much effort.
First of all, remove the break statement in all cases.
Second Print all case values inside all cases. Your code must be looking like.
public function print(String text){
System.out.println(text);
}
switch (myString)
{
case "hello":
print("hello");
case "goodbye":
print("goodbye");
}

complex if( ) or enum?

In my app, I need to branch out if the input matches some specific 20 entries.
I thought of using an enum
public enum dateRule { is_on, is_not_on, is_before,...}
and a switch on the enum constant to do a function
switch(dateRule.valueOf(input))
{
case is_on :
case is_not_on :
case is_before :
.
.
.
// function()
break;
}
But the input strings will be like 'is on', 'is not on', 'is before' etc without _ between words.
I learnt that an enum cannot have constants containing space.
Possible ways I could make out:
1, Using if statement to compare 20 possible inputs that giving a long if statement like
if(input.equals("is on") ||
input.equals("is not on") ||
input.equals("is before") ...) { // function() }
2, Work on the input to insert _ between words but even other input strings that don't come under this 20 can have multiple words.
Is there a better way to implement this?
You can define your own version of valueOf method inside the enum (just don't call it valueOf).
public enum State {
IS_ON,
IS_OFF;
public static State translate(String value) {
return valueOf(value.toUpperCase().replace(' ', '_'));
}
}
Simply use it like before.
State state = State.translate("is on");
The earlier switch statement would still work.
It is possible to seperate the enum identifier from the value. Something like this:
public enum MyEnumType
{
IS_BEFORE("is before"),
IS_ON("is on"),
IS_NOT_ON("is not on")
public final String value;
MyEnumType(final String value)
{
this.value = value;
}
}
You can also add methods to the enum-type (the method can have arguments as well), something like this:
public boolean isOnOrNotOn()
{
return (this.value.contentEquals(IS_ON) || this.value.contentEquals(IS_NOT_ON));
}
Use in switch:
switch(dateRule.valueOf(input))
{
case IS_ON: ...
case IS_NOT_ON: ...
case IS_BEFORE: ...
}
And when you get the value of IS_ON like for example System.out.println(IS_ON) it will show is on.
If you're using Java 7, you can also choose the middle road here, and do a switch statement with Strings:
switch (input) {
case "is on":
// do stuff
break;
case "is not on":
// etc
}
You're not really breaking the concept up enough, both solutions are brittle...
Look at your syntax
"is", can remove, seems to be ubiquitous
"not", optional, apply a ! to the output comparison
on, before, after, apply comparisons.
So do a split between spaces. Parse the split words to ensure they exist in the syntax definition and then do a step-by-step evaluation of the expression passed in. This will allow you to easily extend the syntax (without having to add an "is" and "is not" for each combination and keep your code easy to read.
Having multiple conditions munged into one for the purposes of switch statements leads to huge bloat over time.
Thanks for the suggestions. They guided me here.
This is almost same as other answers, just a bit simplified.
To summarize, I need to compare the input string with a set of 20 strings and
if they match, do something. Else, do something else.
Static set of strings to which input needs to be compared :
is on,is not on,is before,is after, etc 20 entries
I created an enum
public enum dateRules
{
is_on
,is_not_on
,is_before
,is_after
.
.
.
}
and switching on formatted value of input
if(isARule(in = input.replace(" ","_"))
{
switch(dateRule.valueOf(in))
{
case is_on,
case is_not_on,
case is_before, ...
}
}
I copied the formatted value of 'input' to 'in' so that I can reuse input without another replace of '_' with ' '.
private static boolean isARule(String value)
{
for(dateRule rule : dateRule.values())
{
if(rule.toString().equals(value))
{
return true;
}
}
return false;
}
Problem solved.
Reference : https://stackoverflow.com/a/4936895/1297564

Categories

Resources