using enums or static string variable in java - java

got a question around which one is better to use. Java5 Enums or static string.
I always get the data in form of Strings.
So for example,
private static final String LOAD_DRUGS = "load drugs";
or use Java5 enums
public enum LoadType
{
LOAD_DRUGS("load drugs");
}
In my code, I always receive "load drugs" kind of strings. I use if else statements to see what to do next based on it. But I am tending to use java5 enums and use switch case, but I always have to get the enum based of the string value I get.
So what are the pro's and con's of both ways??
Thanks!!

This answer is probably overkill. Maybe there's a badge for that. Anyway, it could be useful in a situation in which you have a lot of enumeration values and have to deal with a Strings as being the means by which another system sends information to you. That is exactly what I have (something north of 50), so I used this construct so that I could generate a mapping just once of the Strings reported by the db and the enums I used internally, and then not think about it after -- toString and fromString do all the work:
package com.stevej;
import com.google.common.collect.HashBiMap;
public enum TestEnum {
ALPHA("Alpha"), BETA("Beta"), GAMMA("Gamma");
private static HashBiMap<TestEnum, String> stringMapping = HashBiMap
.create(TestEnum.values().length);
private String stringValue = null;
TestEnum(String stringValue) {
this.stringValue = stringValue;
}
String getStringValue() {
return this.stringValue;
}
#Override
public String toString() {
return stringMapping.get(this);
}
public static TestEnum fromString(String string) {
return stringMapping.inverse().get(string);
}
static {
for (TestEnum e : TestEnum.values()) {
stringMapping.put(e, e.getStringValue());
}
}
}
Here's a quick test to show the data switching back and forth:
package com.stevej;
public class StackOverflowMain {
public static void main(String[] args) {
System.out.println(">> " + TestEnum.ALPHA);
System.out.println(">> " + TestEnum.BETA);
System.out.println(">> " + TestEnum.GAMMA);
TestEnum a = TestEnum.fromString("Alpha");
TestEnum b = TestEnum.fromString("Beta");
TestEnum c = TestEnum.fromString("Gamma");
System.out.println(">> " + a);
System.out.println(">> " + b);
System.out.println(">> " + c);
}
}
The output shows the use of the mixed case values instead of the uppercase, showing my strings are being used:
>> Alpha
>> Beta
>> Gamma
>> Alpha
>> Beta
>> Gamma
Note that I am using the Google Guava library so I can take advantage of the BiMap.

you can try a simple substitution for turn the string into an enum
switch(MyEnum.valueOf(text.replace(' ', '_')) {
case load_strings:
You can use toUpperCase() if you want it in upper case.
You should do what you think is the simplest and clearest.

Generally you should use Enums to store a set of values that are related in some way. They all should have a similar "is a" relationship. It should not be used to simply store a set of constant strings that are not related.
Of course, if you have a switch statement over a set of string values then that suggests that those string values are related and should be grouped as an Enum and use enum's mechanism to use a switch.
The enum type's valueOf method will allow you to convert from a String to the enum type if the passed string is equal to the name value. If this is not the case, you could implement your own valueOf that uses a stored string field rather than the name. You could store the fields in a private static Map for the conversion.

Because enum is typed.
Static String is not typed.
public enum Something{
CAT("cat");
private String animal;
private Something(String animal){
this.animal = animal;
}
public String getAnimal(){
return this.animal;
}
}

Related

How do I get object and not address without using String/ toString?

I need to create my own String class called MyString without using default String class/vector API. I have to work on some required methods, and their return types are predetermined. I can add other methods as long as String is not used.
Expected use would be:
(at main) System.out.println(str.toLowerCase()) - returns lower case of str
When I want to work with toLowerCase() method with return type MyString, I can't return the object content but only return the address.
Normally, this problem would require modification of toString(), but since this method requires return type of String by default, I can't use modification of toString() for the assignment.
The assignment is supposed to be not so hard and should not require complex extensions. My constructor may be the problem, but I can't specify which part is.
Code
public class MyString {
private char value[];
MyString(char[] arr){
this.value = Arrays.copyOf(arr, arr.length);
}
...
MyString toLowerCase() { // can't change return type
for (int i =0; i<value.length; i++) {
if ((int)value[i] > 64 && (int)value[i] < 91) {
value[i] = (char) (value[i]+32);
}
}
return this; // this returns address, and I can't override toString
}
Problem with System.out.println(str.toLowerCase()) is it ends up calling PrintStream.println(Object o), but that method internally at some point calls o.toString() which uses code inherited from Object#toString() (since you couldn't override toString as it expect as result String which is forbidden in your project) which result in form TypeInfo#hexHashCode.
This means you can't use System.out.println(MyString).
BUT PrintStream (which instance is held by System.out) allows us to provide data to print in different forms. In this case you can use println(char[]). All you need to do is adding to MyString method like toCharArray() which would return (preferably a copy of) array of characters held by MyString class.
This way you can use it like System.out.println(myStringInstance.toCharArray()) so code from your main method would need to look like
System.out.println(str.toLowerCase().toCharArray());
// ^^^^^^^^^^^--this should return char[]
Firstly, the String class is an immutable type, i.e. the methods of String do not change the internal state (i.e. the char array), instead they return a new instance of type String.
To mirror that behavior you could implement something like this:
public MyString toLowerCase() {
char temp = new char[value.length];
// [...] Your code performing the actual logic on temp
return new MyString(temp);
}
The immutability (and its implications) of the String class is very important to understand in practice. For example, the following code procudes the intended result:
String word = "Word";
System.out.println("I can produce upper case (" + word.toUpperCase() + ") " +
"and lower case (" + word.toLowerCase() + ") " +
"without any side-effects on the original (" + word + ").");
However, it's not possible (without "hacky" solutions) to implement a method like this:
void shortenString(String inputAndOutput);
Second, the assignment expects that the class/method must be used as follows:
System.out.println(str.toLowerCase());
The attribute out is effectively a PrintStream, which offers (besides other methods) the following two:
println(Object x) - Prints an Object and then terminate the line.
println(String x) - Prints a String and then terminate the line.
If the method is called with an Object parameter, the internal implementation calls toString() on the given object, thus the only way to satisfy the requirement is to override this method. Unfortunately, this is not allowed by the assignment.
However, if it is not explicitly stated that the solution has to use java.lang.System, you could simply implement your own System class which accepts MyString, e.g.:
public class System {
public static class MyPrintStream /* optional: extends PrintStream */ {
public void println(MyString x) {
java.lang.System.out.println(x.getCharArray());
}
}
public static final out = new MyPrintStream();
}
This would allow you to use it exactly as described in the assignment:
import my.package.System;
public class Main {
public static void main(String[] args) {
// [...] Instantiate str
System.out.println(str.toLowerCase());
}
}

Enum with customized values and Default value

How to retrieve the enum name using value. By passing JOHN I need to retrieve the value as single
public enum Status {
JOHN("single"),
ALEX("married"),
MARTHA("not known");
}
Is it possible to have default value as well in-case value does not match?
To do this you need to define a constructor and a String variable. Then you could create a getter method to return the String variable:
public enum Status {
JOHN("single"),
ALEX("married"),
MARTHA("not known");
private String value;
private Status(String str) {
value = str;
}
public String getValue() {
return this.value;
}
}
And then to get the value you can do:
Status.JOHN.getValue()
To get the enum from a String you can use the valueOf() method:
Status.valueOf("JOHN").getValue();
However this will throw an error if the inputted String does not correspond to an enum. You could either wrap it in a try-catch to assign a default to it:
try {
Status.valueOf("JOHN").getValue();
} catch(IllegalArgumentException e) {
//Assign default
}
However a better design might be to put the possibilities into a HashMap and see if the inputted String is in the HashMap:
Map<String, Status> status = new HashMap<>();
status.put("JOHN", Y.JOHN);
if(status.containsKey(input)) {
//Do stuff
} else {
//Assign default
}
Define your enum like
enum Status {
JOHN("single"), ALEX("married"), MARTHA("not known");
private String value;
private Status(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Now to fetch the value :
System.out.println(Status.JOHN.getValue());
Here define a parameterized constructor for each enum and store this value in a member variable.
A Java enum is an object, just like other objects in most ways. You can give it fields, methods, and constructors.
In this case, you can provide a marriage field and set it in a constructor just like you would for any other object.
Check out the official page on Java enums to see official examples, specifically the example with planets for your case. The Java Tutorials: Enum Types
Note also that you might not want to represent this specific data as an enum. You will be limited to only those 3 specific people by doing so. This is only appropriate if you know that you definitely will have only this specific list of people for the entire lifetime of this code. Otherwise you should just use a normal object.
A better idea might be to have a marriage enum and give your people a field of that type.
public enum MarriageStatus
{
Married,
NotMarried,
Unknown;
}
public class Person
{
String name = "Unknown";
MarriageStatus marriageStatus = MarriageStatus.Unknown;
}

How to access constants from a java class through other variable?

I have java class which has all constants as given below:
public final class CategoryIDs {
public static final String Extraction_of_natural_gas = "1111";
public static final String Mining_of_hard_coal = "2222";
public static final String Mining_of_iron_ores = "3333";
public static final String Mining_of_lignite = "4444";
}
Now I want to access these constants in some other class through a variable which holds name of the variable.
For example:
String temp = "Extraction_of_natural_gas";
Using this temp variable I want to access constants from above class. But I can't do CategoryIDs.temp as it isn't allowed. So what is the best way to achieve this?
You can use reflection to extract it:
String value = (String) CategoryIDs.class.getField(temp).get(null);
The null argument passed to get signifies this is a static field, and no instance is required in order to get its value.
Note that this technique is very error prone. The code above doesn't contain any error checking or exception handling in order to make it easier to read, but a real application should probably contain them.
If you really, really need to do this with your current structure, you could use reflection.
However, you may well find that an enum would be a better fit. Something like:
public enum CategoryID {
EXTRACTION_OF_NATURAL_GAS("Extraction_of_natural_gas", 1111),
MINING_OF_HARD_COAL("Mining_of_hard_coal", 2222),
MINING_OF_IRON_ORES("Mining_of_iron_ores", 3333),
MINING_OF_LIGNITE("Mining_of_lignite", 4444);
private static final Map<String, CategoryID> keyMap;
static {
keyMap = new HashMap<>();
for (CategoryID category : CategoryID.values()) {
keyMap.put(category.getKey(), category);
}
}
private final String key;
private final int value;
public int getValue() {
return value;
}
public String getKey() {
return key;
}
private CategoryID(String key, int value) {
this.key = key;
this.value = value;
}
public static CategoryID fromKey(String key) {
return keyMap.get(key);
}
}
Benefits of this approach:
You separate the name in the code (which is now more conventional for Java) from the key you'd provide in the data. That means you can use whatever keys you'd like in the data, including ones with spaces in etc. They no longer need to be valid identifiers.
You still get compile-time safety when you refer to the enum values from code.
You get better safety as you'll never accidentally use a string instead of a category ID, which you could easily have done using your constants.
I think you are looking for introspection. This is the "read only" part of reflection, in which you can retrieve the value of an object.
The code would be as follows, pieced for ease of understanding:
Object value = null;
String constName = "Bar";
Field constant = Foo.class.getField( constName );
if ( constant != null ) {
value = constant.get( null );
}
if ( value != null ) {
System.out.println( value );
} else {
System.out.println( "Constant " + constName + " was not found." );
}
Interestingly, you can access the value from the Field object calling get(), and you pass null because the constant is static, and thus you don't have an instance to extract the value from.
You can find the whole code here: http://ideone.com/v4fcvH
Hope this helps.

bad operand types for binary operator '+' during printing enum

I'm working with JavaFX application and trying to create separate enum with all paths. Here is the code.
enum Paths {
STYLE_SHEETS("../stylesheets"),
CONTROLLER("../controller"),
CONNECTION("../connection"),
RESOURCE("../resource"),
VIEWS("../views"),
APPLICATION("../application"),
MOVE_UP("..");
private final String path;
private Paths(final String value) {
this.path = value;
}
#Override
public String toString() {
return path;
}
}
public class Test{
public static void main(String[] args){
System.out.println(Paths.MOVE_UP + Paths.VIEWS);
}
}
But when I try to combine two enum's instance I got the error.
Test.java:29: error: bad operand types for binary operator '+'
System.out.println(Paths.MOVE_UP + Paths.VIEWS);
^
first type: Paths
second type: Paths
1 error
1) Why I'm getting this error?
2) Is it a good way to create enum of paths and use in controller class where you want to access the directory. ( This is optional question. Answering this question will be bonus for me. )
you can solve this pretty quick bye adding the toString() Methode.
Why do you Need the toString() Methode?
Because it would be ambiguous. Java only converts + to String#append, when it knows at least one part should be a string. – #Silverclaw
Updated Code:
enum Paths {
STYLE_SHEETS("../stylesheets"),
CONTROLLER("../controller"),
CONNECTION("../connection"),
RESOURCE("../resource"),
VIEWS("../views"),
APPLICATION("../application"),
MOVE_UP("..");
private final String path;
private Paths(final String value) {
this.path = value;
}
#Override
public String toString() {
return path;
}
}
public class test{
public static void main(String[] args){
System.out.println(Paths.MOVE_UP.toString() + Paths.VIEWS.toString());
}
}
Edited to Complete:
For more understanding, Java needs to solve + operator first then it will pass to System.out.println(); So as you expecting toString() should be invoke, it will not call because no one enum is going to println(); they need to solve + operator first, and Enum have nothing with + that's why you are getting this error.
You can't use the + operator between two enum values, as the error says. At least of the operands should be a string in order for Java to implicitly convert the other one to a string too. E.g.:
public static void main(String[] args){
System.out.println(Paths.MOVE_UP.toString() + Paths.VIEWS);
// Here -------------------------^
}
Either you explicitly call the toString() method
System.out.println(Paths.MOVE_UP.toString() + Paths.VIEWS.toString());
or you add an empty string in front of your enums (as you'd do to concatenate integers instead of adding them up)
System.out.println("" + Paths.MOVE_UP + Paths.VIEWS);
The behaviour is specified in JLS Sec 15.18 "Additive Operators":
If the type of either operand of a + operator is String, then the operation is string concatenation.
Otherwise, the type of each of the operands of the + operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or a compile-time error occurs.
Neither of your operands is a String; neither of your operands is convertible to a primitive numeric type. So:
a compile-time error occurs
You can either force (at least) one of the operands to be a String:
Paths.MOVE_UP.toString() + Paths.VIEWS
or pre-concatenate with the empty string:
"" + Paths.MOVE_UP + Paths.VIEWS
which is equivalent to
("" + Paths.MOVE_UP) + Paths.VIEWS

Enum relating to a string array that was read from file

I've received a working code (in Java, 1.7) that does the following:
load an array of strings (a list of blood test names) from a file into a string array member (using Properties and FileInputStream). The file can change the strings but the meaning stays the same (for example: a test can be called "abc" and in another run it is called "zzz"). I've got an enum class that enumerates the test names. The enum strings aren't the same as the inputted strings (since the latter can change).
file bloodtest.names contains:
bloodTestNames=abc;def;123;
code:
public enum BloodTestNames {
AAA,BBB,CCC;
}
Properties props = new Properties();
FileInputStream fis = new FileInputStream("bloodtest.names");
props.load(fis);
String testName[]=props.getProperty("bloodTestNames").toString().split(";");
Now to the questions:
Question 1:
I need to return the string that was set in the file when I know the test name (for instance: return "def" for value BBB). What's the best of doing that?
the best way I've come up with is:
return testName[BloodTestNames.BBB.ordinal()]
Question 2: if BBB is not known in compile time - how do I accomplish the same target?
Three points:
* I'm a veteran at C but a newbie with Java. Any Do's and Don't are welcome. Assume my Java knowledge is zero.
* I don't total re-factoring is that's what's needed here.
* I've probably forgot to mention important details, please ask and I'll feel the missing gaps
I'll first assume you do need enum constants for modeling this use-case because you have some sort of specific code to be executed for each kind of blood test (otherwise, a simple set of strings would be enough and more flexible, since you don't need to know the number of tests upfront or care about their names).
Q1: Since Java enums are a little more than a sequence of values, you can make full use of their object oriented nature.
public enum BloodTest {
AAA, BBB, CCC;
private static String[] names;
public static void setNames(String[] names) {
if (BloodTest.names != null)
throw new IllegalStateException("You can only set the names once");
if (names.length != values().length)
throw new IllegalArgumentException("Wrong number of names");
BloodTest.names = names;
}
#Override
public String toString() {
return names[ordinal()];
}
}
Now all you need to do is to initialize your enum by calling BloodTest.setNames(namesFromConfiguration) and then you can get the string representation of each constant by calling the standard toString() method on it: BloodTest.BBB.toString().
Since the initial assumption was that you have some specific logic for each of the test types, I would suggest that logic (as well as the required properties) will also be encapsulated in the enum itself or the enum constants; e.g.:
public enum BloodTest {
AAA(10) {
#Override
public boolean isRequired(MedicalRecord medicalRecord) {
return medicalRecord.includes("someDisease");
}
},
BBB(15) {
#Override
public boolean isRequired(MedicalRecord medicalRecord) {
return ! medicalRecord.hasTakenBloodTestsLately();
}
},
CCC(20) { // ... also implements the abstract method and so on
private final int threshold;
private BloodTest(int threshold) {
this.threshold = threshold;
}
public boolean hasPassed(int value) {
return value <= threshold;
}
public abstract boolean isRequired(MedicalRecord medicalRecord);
// ... same as above
}
Now, once you get a reference to some BloodTest, you can check whether that specific test passed by invoking the corresponding method without switching and having the logic spread around the client code:
BloodTest bloodTest = BloodTest.valueOf(someString); // someString can be "AAA", "BBB" or "CCC"
// no matter which constant this is, you use it as an object and rely on polymorphism
if (bloodTest.hasPassed(someValue)) { // ... do something
Q2: Your question 2 kind of "questions" my initial assumption regarding your actual need for an enum. If there's a chance you'll need to dynamically handle blood tests that you don't know about yet, then you can't use an enum.
In other words, if your code does not have any switch or if/else if blocks to handle each blood test, an enum is a really bad choice for your use case.
However, if it does, than I'd recommend refactoring the code to include the logic in the enum itself as in the above example, rather than in switch/if blocks; moreover, if your switch has a default case (or your if has a final else block), this can still be modeled in the enum itself, for instance by adding a DEFAULT constant as a fallback.
Make the whole thing settings driven: Add a statuc method to load in settings of what string maps to what enum and add a factory method that uses these settings:
public enum BloodTestNames {
AAA,BBB,CCC;
private static Map<String, BloodTestNames> map = new HashMap<String, BloodTestNames>();
public static void addAlias(String alias, String name) {
map.put(alias, valueOf(name));
}
public static BloodTestNames getByAluas(String alias) {
if (map.containsKey(alias))
return map.get(alias);
// own name assumed to be mapped
return valueOf(alias);
}
}
On startup, repeatedly call BloodTestNames.addAlias() based on some settings file to load the mappings.
When you're reading the saved file, use BloodTestNames.getByAlias() to return the enum for a given string value.
You would do well to name your class in the singular, and drop "Name", ie BloodTest - name the class for what each enum is (all enums have a "name" which is the coded instance name).
A short extract from one of my enum class :
public enum TypesStructurelsE {
SOURCE("SRC"),
COLONNE("COL");
private String code;
TypesStructurelsE(final String code1) {
code = code1;
}
/** #return String */
public String getCode() {
return code;
}
public void setCode(final String newCode) {
code = newCode;
}
}
. . In other class
if(TypesStructurelsE.SOURCE.getCode().equal(testName[i])){ // can be "COL" or "SRC"
//
;
}
... changing value :
TypesStructurelsE.SOURCE.setCode("SOURCE_NEW");
So, if your properties file change, you have just to compile with the new symbole (SRC --> SOURCE) no more

Categories