Not sure how I'm going to attack this.
Basically what I have is input of varying length, one or multiple times, that will cause an action. It being from typed input, file etc.
I have no idea on by what and how to tackle this. Would it be best to have a function returning an int that correspond to an public static final int FOO = 1;, an enum, an other way?
What I have as of now is a series of if statements as in:
if (str.equals("foo") || str.equals("F")) {
blah;
} else if (str.equals("beach")) {
more blah;
}
Is this good as any, or is there a better way? Have had a peek at enum but seems like that is more to it then in e.g. C. This is probably wrong, but would it be something in the direction of this?
class Mother
{
HappyCamping() {
switch (ValInput(str)) {
case FOO: do fo; break;
case BAR: do bar; break;
case BAZ: do fo bar: break
...
}
private enum ValInput(String str)
{
FOO("foo"), BAR("bar"), BAZ("baz");
private int value;
private ValInput(String str) {
if (str.equals("blah"))
this.value = 1;
...
}
}
}
Point being having a cleaner approach and separate out the "parsing" from the main routine. What would be a good way here?
One approach would be to write a parser that returns tokens. The tokens could be represented by ints or Enums. That modularizes your code in a way you suggest you want.
The other way is to use enums.
public enum Token {
FOO("foo", "f"),
BAR("bar", "b");
private String keyword;
private String abbreviation;
private Token(String keyword, String abbreviation) {
this.keyword = keyword;
this.abbreviation = abbreviation;
}
public String getKeyword() {
return this.keyword;
}
public String getAbbreviation() {
return this.abbreviation;
}
public static Token valueOf(String s) {
for (Token token : values()) {
if (token.getKeyword().equals(s) || token.getAbbreviation().equals(s)) {
return token;
}
}
throw new IllegalArgumentException("No such keyword: " + s);
}
}
Then you can do something like:
switch (Token.valueOf(inputString)) {
case BAR : doBarStuff(); return;
case FOO : doFooStuff(); return;
}
Is this good as any, or is there a better way? Have had a peek at enum but seems like that is more to it then in e.g. C. This is probably wrong, but would it be something in the direction of this?
Then go learn how enum works. Don't avoid a feature that may solve your problem just because it has more features. Chances are your design will want to make use of Java-style enums if you want a fixed set of actions.
enum Action { CLICK("click"), CLEAR("erase"); }
etc. is a good start.
Java (I think since 6, possibly 7) also supports switch taking strings instead of integer or enum values.
Not sure that I understand the entire problem, but you can convert a string to an enum easily in Java.
If the entry strings are limited and predefined, and you want to parse it as an enum using
EnumType.valueOf("foo")
I recommend reading on Java enums, they are quite powerful compared to C enums.
In Java 7 you can use String in a switch expression. Refer to this article:
switch (str) {
case "blah":
// some action
break;
case "beach":
// another blah
break;
default:
// default action
break;
}
Your if statements seems the most logical way to tackle this problem. No need to make your life complex, just keep it simple. Any other method has trade offs and complexity.
Consider using a Map and the command pattern as shown here. You can hide all map a keyword to functionality and never have to use an if or switch.
Related
Goal:
Represent subset of Strings created from Strings defined in abstract class
test if string on input belongs to given subset
Initial solution:
Let's have list of possible events.
/**
* List of events.
*/
public abstract class EventKeys {
public static final String KEY_EVENT_1 = "EVENT_1";
public static final String KEY_EVENT_2 = "EVENT_2";
public static final String KEY_EVENT_3 = "EVENT_3";
public static final String KEY_EVENT_4 = "EVENT_4";
public static final String KEY_EVENT_5 = "EVENT_5";
public static final String KEY_EVENT_6 = "EVENT_6";
public static final String KEY_EVENT_7 = "EVENT_7";
//etc ..
}
I want make subset of these events for example events 1,3,5 and only for these events allow some action. The goal is make method boolean isEventAllowed(String eventKey) which will say if event belongs to subset of allowed events.
The really naive way to do this is:
/**
* Allow only events 1,3,5
*/
private isEventAllowed(String eventKey) {
if(eventKey.equals(EventKeys1.KEY_EVENT_1)) {
return true;
} else if(eventKey.equals(EventKeys1.KEY_EVENT_3)) {
return true;
} else if(eventKey.equals(EventKeys1.KEY_EVENT_3)) {
return true;
} else {
return false;
}
}
The I feel this approach is not very convinient. I need better way to represent the subset of strings and provide action does input string belongs to defined subset?
Other possible solutions:
As other options i was thinking about other two options, but I'm still not sure if its good way to do it.
1)enum - create enum of strings
Put in enum: EventKeys1.KEY_EVENT_1, EventKeys1.KEY_EVENT_2, EventKeys1.KEY_EVENT_3
Test does String keyEvent belons to defined enum?
2) list
create list List<String> subset and put there
EventKeys1.KEY_EVENT_1, EventKeys1.KEY_EVENT_2, EventKeys1.KEY_EVENT_3
test if String keyEvent belongs to list subset
PLEASE READ THIS BEFORE ANSWER:
class EventKeys is given, can't be changed, main set of options
I need somehow represent subset
I need advice for better implementation of method isAllowedEvent(String keyEvent) which returns true if input string
belongs to defined subset
How about something like this?
private boolean isEventAllowed(String eventKey) {
return Arrays.asList(KEY_EVENT_1, KEY_EVENT_3, KEY_EVENT_5).contains(eventKey);
}
Readability could be improved following John Fergus' comment by using something like this:
private static List<String> SUBSET = Arrays.asList(KEY_EVENT_1, KEY_EVENT_3, KEY_EVENT_5);
private boolean isEventAllowed(String eventKey) {
return SUBSET.contains(eventKey);
}
While a Set holding the allowed values is usually the preferred option, there are also possible syntactical improvements for your original code which you should become aware of, as their general pattern applies to other situations as well.
A statement like
if(condition1)
action;
else if(condition2)
/* (same) */ action;
…
is redundant and may be replaced by
if(condition1 || condition2)
action;
…
similarly
if(condition)
return true;
else
return false;
is redundant and may (or even should) be replaced by
return condition;
Putting both together, your original code becomes
private boolean isEventAllowed(String eventKey) {
return eventKey.equals(EventKeys1.KEY_EVENT_1)
|| eventKey.equals(EventKeys1.KEY_EVENT_3)
|| eventKey.equals(EventKeys1.KEY_EVENT_5);
}
Alternatively, you can use a switch statement:
private boolean isEventAllowed(String eventKey) {
switch(eventKey) {
case EventKeys1.KEY_EVENT_1:
case EventKeys1.KEY_EVENT_3:
case EventKeys1.KEY_EVENT_5:
return true;
default:
return false;
}
}
Not everyone likes this coding style, but that’s more an issue of project or company policies. There are situation, where such a switch statement still is the cleanest solution. One advantage over if statements and even the Set approach is that the compiler will immediately shout if you mistakenly name the same constant twice rather than the intended constant (a typical copy&paste error), like you do in your third if statement where you use KEY_EVENT_3 instead of the intended KEY_EVENT_5…
Most people understand the innate benefits that enum brings into a program verses the use of int or String. See here and here if you don't know. Anyway, I came across a problem that I wanted to solve that kind of is on the same playing field as using int or String to represent a constant instead of using an enum. This deals specifically with String.format(...).
With String.format, there seems to be a large opening for programmatic error that isn't found at compile-time. This can make fixing errors more complex and / or take longer.
This was the issue for me that I set out to fix (or hack a solution). I came close, but I am not close enough. For this problem, this is more certainly over-engineered. I understand that, but I just want to find a good compile-time solution to this, that provides the least amount of boiler-plate code.
I was writing some non-production code just to write code with the following rules.
Abstraction was key.
Readability was very important
Yet the simplest way to the above was preferred.
I am running on...
Java 7 / JDK 1.7
Android Studio 0.8.2
These are unsatisfactory
Is there a typesafe alternative to String.format(...)
How to get string.format to complain at compile time
My Solution
My solution uses the same idea that enums do. You should use enum types any time you need to represent a fixed set of constants...data sets where you know all possible values at compile time(docs.oracle.com). The first argument in String.format seems to fit that bill. You know the whole string beforehand, and you can split it up into several parts (or just one), so it can be represented as a fixed set of "constants".
By the way, my project is a simple calculator that you probably seen online already - 2 input numbers, 1 result, and 4 buttons (+, -, ×, and ÷). I also have a second duplicate calculator that has only 1 input number, but everything else is the same
Enum - Expression.java & DogeExpression.java
public enum Expression implements IExpression {
Number1 ("%s"),
Operator (" %s "),
Number2 ("%s"),
Result (" = %s");
protected String defaultFormat;
protected String updatedString = "";
private Expression(String format) { this.defaultFormat = format; }
// I think implementing this in ever enum is a necessary evil. Could use a switch statement instead. But it would be nice to have a default update method that you could overload if needed. Just wish the variables could be hidden.
public <T> boolean update(T value) {
String replaceValue
= this.equals(Expression.Operator)
? value.toString()
: Number.parse(value.toString()).toString();
this.updatedString = this.defaultFormat.replace("%s", replaceValue);
return true;
}
}
...and...
public enum DogeExpression implements IExpression {
Total ("Wow. Such Calculation. %s");
// Same general code as public enum Expression
}
Current Issue
IExpression.java - This is a HUGE issue. Without this fixed, my solution cannot work!!
public interface IExpression {
public <T> boolean update(T Value);
class Update { // I cannot have static methods in interfaces in Java 7. Workaround
public static String print() {
String replacedString = "";
// for (Expression expression : Expression.values()) { // ISSUE!! Switch to this for Expression
for (DogeExpression expression : DogeExpression.values()) {
replacedString += expression.updatedString;
}
return replacedString;
}
}
}
So Why Is This An Issues
With IExpression.java, this had to hacked to work with Java 7. I feel that Java 8 would have played a lot nicer with me. However, the issue I am having is paramount to getting my current implementation working The issue is that IExpression does not know which enum to iterate through. So I have to comment / uncomment code to get it to work now.
How can I fix the above issue??
How about something like this:
public enum Operator {
addition("+"),
subtraction("-"),
multiplication("x"),
division("÷");
private final String expressed;
private Operator(String expressed) { this.expressed = expressed; }
public String expressedAs() { return this.expressed; }
}
public class ExpressionBuilder {
private Number n1;
private Number n2;
private Operator o1;
private Number r;
public void setN1(Number n1) { this.n1 = n1; }
public void setN2(Number n2) { this.n2 = n2; }
public void setO1(Operator o1) { this.o1 = o1; }
public void setR(Number r) { this.r = r; }
public String build() {
final StringBuilder sb = new StringBuilder();
sb.append(format(n1));
sb.append(o1.expressedAs());
sb.append(format(n2));
sb.append(" = ");
sb.append(format(r));
return sb.toString();
}
private String format(Number n) {
return n.toString(); // Could use java.text.NumberFormat
}
}
I'm beginner at java, and I'm making a simple program where I type in something, and if what I type in matches one of the things on the "database" then it'll print some text. Is there a simpler way to check this rather than doing this:
int 1;
int 2;
int 3;
etc.
if([USER INPUT].equals("1")) {
System.out.println("TEST");
}
400 times.
Use a switch statement or a HashMap.
Switch statement: Readable, but compiles similarly (if not identically) to an if-else chain.
switch([USER_INPUT]) {
case 1:
System.out.println("TEST");
break;
case 2:
System.out.println("HELLO");
break;
// And so on.
}
Hash Map: Much more readable and simpler. This is preferred.
// Initialization.
Map<Integer,String> map = new HashMap<Integer,String>();
map.put(1,"TEST");
map.put(2,"HELLO");
// Printing.
String s = map.get(USER_INPUT);
if (s == null)
System.out.println("Key doesn't exist.");
System.out.println(s);
Use a HashMap, with key as Integer, and value as text.
System.out.println(myMap.get(USER_INPUT));
Where you have done myMap.put(1, "TEST"); etc, this keeps your code much OO.
the underlying bytecode of switch and if are very comparable, and personally don't see any advantage of switching to switch (unless you want fall through, which means don't include break statement).
A fun alternative would be to use an enum. This would work if you want to define all of the values in a class. It would simplify the code used to get the text value. And it gives you some more fun options beyond what a switch statement would give you.
enum NumberText {
HELLO(1),
WORLD(2);
private static final HashMap<Integer,NumberText> map = new HashMap<Integer,NumberText>();
static{
for (ConnectionGenerator c : ConnectionGenerator.values()) {
map.put(c.code, c);
}
}
Integer code;
NumberText(Integer pCode) {
this.code = pCode;
}
Static ConnectionGenerator getTextFor(Integer code) {
return map.get(code);
}
}
Then to get the text, simply do this:
NumberText nt = NumberText.getTextFor(USER_INPUT);
System.out.println(nt);
You can get fancier and put an additional constructor variable into the enum and have a specific string of text.
enum NumberText {
HELLO(1, "Hello to You"),
GOODBYE(2, "Goodbye");
private static final HashMap<Integer,NumberText> map = new HashMap<Integer,NumberText>();
static{
for (ConnectionGenerator c : ConnectionGenerator.values()) {
map.put(c.code, c);
}
}
Integer code;
String text;
NumberText(Integer pCode, String pText) {
this.code = pCode;
this.text = pText;
}
ConnectionGenerator getNumberTextFor(Integer code) {
return map.get(code);
}
getText() {
return this.text;
}
}
Then you could get the text like this:
NumberText.getNumberTextFor(USER_INPUT).getText();
Use a switch statement.
switch(i){
case 1:
System.out.println("Hi");
break;
case 2:
System.out.println("Ok");
break;
// ...
}
You can use a switch statement.
Here's a quick tutorial and some more in-depth explanation.
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html
Is it possible to use an enumeration in the following circumstance:
Let’s say you have a certain amount of predefined 'read types'. Example read types could be: Diagnostic, KWH, MaxDemand, OnPeak, etc. And for each of these read types, there’s a ‘TIMTagNumber’ which is essientally a protocol for retrieving each predefined read type.
For example, TIMTagNumber 1100 would retrieve the read type Diagnostic
TIMTagNumber 1300 would retrieve the read type KWH.
The problem is that a predefined read type can sometimes be retrieved by more than one TIMTagNumber.
I want to create an enumeration ReadType that would define each read type and all TIMTagNumbers that can be used to retrieve that read.
Can you use an enumeration in this way?
public enum ReadType{
KWH(1300)
Diagnostic(1100)
ReadType3(1400, 1401) // This read can be retrieved by both 1400 and 1401
}
If an enumeration is not the way to go, is there an elegant or efficient way to define these read types? The overall desired outcome of all this essientally is being recognizing what type of read it is based on the TIMTagNumbers.
I.E. Given 1400 OR 1401 you would know that it's 'ReadType3'.
Can you do this? Yes. Whether it's the right decision will depend on whether you want to couple these TIMTagNumbers to the read type. If not, a simple Map<Integer, ReadType> will probably suffice.
Here's how you could do it:
public static enum MyEnum {
KWH(1300),
Diagnostic(1100),
ReadType3(1400, 1401);
private Set<Integer> timTagNumbers;
MyEnum(Integer... timTagNumbers) {
this.timTagNumbers = new HashSet<Integer>(Arrays.asList(timTagNumbers));
//add check to make sure that values are unique across all instances
}
public static MyEnum forTIMTagNumber(int num) {
for ( MyEnum readType : values() ) {
if ( readType.timTagNumbers.contains(num) ) {
return readType;
}
}
throw new NoSuchElementException("No ReadType matching TIMTagNumber " + num);
}
}
//...
int timTagNumber = 1400;
ReadType readType = ReadType.forTIMTagNumber(timTagNumber);
As I said above, this style works well when the data and the enum types are intrinsically coupled already. It would not be good for when the enum type is decoupled from the mapped values (e.g. the values are used for one of many ways of serializing the enum) or if the values are configuration-specific or even dynamic (e.g. if they were prices on an item). In these cases it is usually best to externalize this mapping in an EnumMap or Map.
public enum ReadType {
KWH(1300),
Diagnostic(1100),
ReadType3(1400, 1401);
private int[] timTagNumbers;
private ReadType(int ... numbers) {
this.timTagNumbers = numbers;
}
public int[] getTimTagNumbers() {
return timTagNumbers;
}
public static ReadType forTimTagNumber(int n) {
for (ReadType type : values()) {
if (Arrays.binarySearch(type.timTagNumbers, n) != -1) {
return type;
}
}
throw new NoSucheElementException(); // if not found
}
With this you can do
int[] timTagNumbers = ReadType.Diagnostic.getTimTagNumbers(); // [ 1100 ]
and
ReadType type3 = ReadType.forTimTagNumber(1401); // ReadType.ReadType3
You can indeed use enumerations in that way, but your example is missing a private field and a constructor.
Something like:
public enum Bla{
CASE1(100),CASE2(200);
private int amount;
private Bla(int amount) {
this.amount = amount;
}
public Bla getByValue(int value){
switch (value) {
case 100: return CASE1;
case 200: return CASE2;
}
return null;
}
}
I've included a "reverse lookup" method that returns an Enum given the value.
The main advantage is that you can have the rest of your code using "Bla" instead of int's which will guarantee type-safety on your operations, basically, it'll make impossible to pass an invalid int value as a method parameter (and you can use switch statements over enums too, and that's pretty awesome in some usage scenarios).
EDIT: I noticed after I posted that you need more then one int to specify the Enum, but the same logic applies, with the due changes in the methods, of course.
You could do something like the following, when you supply values in the parentheses where the enum variable is declared, it is calling the constructor of the enum. You need to create a different method in the enum itself to get the enum type from the integer value. See below.
public enum ReadType {
KWH(), DIAGNOSTIC(), READTYPE3();
public ReadType getReadType(int num) {
ReadType toReturn = KWH;
switch (num) {
case 1300:
toReturn = KWH;
break;
case 1100:
toReturn = DIAGNOSTIC;
break;
case 1400:
toReturn = READTYPE3;
break;
case 1401:
toReturn = READTYPE3;
break;
}
return toReturn;
}
If you can impose some restrictions like no more than 2 tags can be associated with a read type and each tag is no greater than 2^15, then you can store the two numbers into 1 integer. See this S/O post for more details.
I have some enums like this:
public enum Classification {
UNKNOWN("Unknown"),
DELETION("Deletion"),
DUPLICATION("Duplication"), ....
but some of them have like 20 members, so currently in code I deal with them with huge if/else blocks like this:
int classification= rs.getInt("classification");
if (classification == Classification.UNKNOWN.ordinal()) {
variant.setClassification(Classification.UNKNOWN);
} else if (classification == Classification.DELETION.ordinal()) {
variant.setClassification(Classification.DELETION);
( rs is from JDBC tho).
Does Java have a better way this these big if/else blocks to do what I am doing? some sorting of looping through it?
You could use Enum#values() to get all enum values in an array. The ordinal maps 1:1 to the array index. Add the following method fo your Classification enum:
public static Classification of(int ordinal) {
if (0 <= ordinal && ordinal < values().length) {
return values()[ordinal];
}
throw new IllegalArgumentException("Invalid ordinal " + ordinal);
}
and use it as follows
Classification classification = Classification.of(rs.getInt("classification"));
// ...
However, using enum's ordinal for this is not the best practice. What if some developer rearranges the enum's values or adds/removes values? Even the javadoc warns that it has usually no use for developers. Rather give each enum value a fixed identifier. You could pass it in as an additional argument of the enum constructor argument. You could even use enum's String representation for that.
UNKNOWN(1, "Unknown"),
DELETION(2, "Deletion"),
DUPLICATION(3, "Duplication"),
// ...
Then use that value for DB instead and modify the of() method to walk through them in a foreach loop:
public static Classification of(int id) {
for (Classification classification : values()) {
if (classification.id == id) {
return classification;
}
}
throw new IllegalArgumentException("Invalid id " + id);
}
If the db value is the ordinal of the Enum then:
int classification= rs.getInt("classification");
variant.setClassification(Classification.values()[classification]);
I'll leave bounds checking as an exercise for the reader.
You can loop through an enumeration’s values via the object the someEnum.values() method returns:
for (Classification clz : Classification.values()) doSomethingWith(clz);
found here
I don’t know how exactly I can help you, since i don’t know what rs.getInt(String) does.
It seems to give back an Integer representing a enum value of Classification, but why?
Use variant.setClassification(YourEnumClassHere.values()[classification]). Enum.values() returns an array of all the declared enums in that class.
Instead of storing ordinal, you can store the name and use the valueOf method to convert the String back to your Enum type.
If you willing and able to store a string representation (this is a good technique) of the ENUM in your database, see Reference from Gareth Davis in comments above. If you are unwilling and/or unable to store a string representation and must continue with an ordinal representation, I suggest that a Map is called for. Here is some example code:
public class EnumMap
{
private enum FistSounds
{
Blam, Kapow, Zowie, Biff;
private static Map<Integer, FistSounds> ordinalMap = new HashMap<Integer, FistSounds>();
static
{
ordinalMap.put(Blam.ordinal(), Blam);
ordinalMap.put(Kapow.ordinal(), Kapow);
ordinalMap.put(Zowie.ordinal(), Zowie);
ordinalMap.put(Biff.ordinal(), Biff);
}
public static final FistSounds getByOrdinal(final int enumIndex)
{
return ordinalMap.get(enumIndex);
}
}
public static void main(String[] args)
{
FistSounds fistSound;
for (int index = -1; index < 5; ++index)
{
fistSound = FistSounds.getByOrdinal(index);
System.out.print("Ordinal: ");
System.out.print(index);
System.out.print(", FistSound: ");
System.out.println(fistSound);
}
}
}
I'd recommend using a switch statement, if the logic to execute is different for each case....
do as #Gareth Davis instructs and then just have a switch statement and handle each case as required.
Enums are also eligible to be used in switch statements see here