I guess I did not yet get the concept of Enums in Java.
I try to compare Strings to my Enum-entries comparable to Special characters in an enum.
package com.stackoverflow.tests;
public class EnumTest {
enum EnumTestOperator {
EQUAL("=="),
NOT_EQUAL("!=");
private String value;
private EnumTestOperator(String value) {
this.value = value;
}
public String toString() {
// will return == or != instead of EQUAL or NOT_EQUAL
return this.value;
}
}
public static void main(String[] args) {
String line;
line = "<>";
line = ".ne.";
line = "!=";
// Operator
switch(line) {
// case "!=":
case EnumTestOperator.NOT_EQUAL:
System.out.println("Not Equal");
break;
default:
System.out.println("Something else");
break;
}
}
}
But in the Line:
case EnumTestOperator.NOT_EQUAL:
I get a compiler error:
Type mismatch: cannot convert from EnumTest.EnumTestOperator to String
What am I doing wrong?
You are comparing incompatible types. It's similar to the statement
"some string" == EnumTestOperator.NOT_EQUAL
where your compare a string value to an enum constant.
Just stop using enums and declare them as string constants:
private static final String EQUAL = "==";
private static final String NOT_EQUAL = "!=";
Then you can use them in the switch/case.
The other solution would be to find the enum constant based on your input. Add this to your enum:
public static EnumTestOperator byValue(String val){
for(EnumTestOperator en:values()){
if(en.value.equals(val)){
return en;
}
}
return null;
}
And then use the function like this:
EnumTestOperator en = EnumTestOperator.byValue(line);
switch(en) {
case NOT_EQUAL:
System.out.println("Not Equal");
break;
default:
System.out.println("Something else");
break;
}
Unfortunately you will have to handle the null case differently since putting a null into a switch will throw a NullPointerException.
EnumTestOperator.NOT_EQUAL is an instance of EnumTestOperator. You cannot compare it with a String.
You can evenutually compare EnumTestOperator.NOT_EQUAL.toString() with a String but not in a case statement (case statements require constant expressions in Java).
But you can instead iterate on EnumTestOperator constants :
public static EnumTestOperator find(String strValue) {
for(EnumTestOperator operator : EnumTestOperator.values()) {
if(operator.toString().equals(strValue)) {
return operator;
}
}
throw new IllegalArgumentException("No matching value");
}
Related
I've class Aligment like below:
public enum Aligment
{
Evil,
Neutral,
Good,
Undefined
}
And I want to use these values in switch like so:
System.out.print("Choose you'r start up character" +
"1.Good" +
"2.Evil" +
"3.Neutral");
//1 string alignmentChoice = scan.nextLine();
//2 Aligment alignmentChoice = Aligment.Undefined;
switch( aligmentChoice )
{
case Good:
alignment = Aligment.Good;
break;
case Evil:
alignment = Aligment.Evil;
break;
case Neutral:
alignment = Aligment.Neutral;
break;
default:
System.out.println("How did you manage to get here? You have broke the system.");
break;
}
And I'm not sure how to use it like //1 or //2. Thanks for help in advance.
Use it like so :
switch(Aligment.valueOf(alignmentChoise)) {
case Evil:
alignment = Aligment.Evil;
break;
}
Note :
This will throw IllegalArgumentException if the enum constant is not found.
If you refactor and move this switch case logic to a method in your enum, you no longer need to use a switch statement for getting an enum from a string.
In the following example, I have added a method fromString() which will take a string input name, and compare with all of our enum values (case unsensitive).
If a matching value is not found, we throw an IllegalArgumentException.
Here is the example:
public class AlignmentTest{
public static void main(String[] args){
String good = "good";
String neutral = "NEUTRAL";
String evil = "EvIl";
String unknown = "unknown";
Alignment alignment1 = Alignment.fromString(good);
System.out.println("Alignment 1: " + alignment1);
Alignment alignment2 = Alignment.fromString(neutral);
System.out.println("Alignment 2: " + alignment2);
Alignment alignment3 = Alignment.fromString(evil);
System.out.println("Alignment 3: " + alignment3);
Alignment alignment4 = Alignment.fromString(unknown);
System.out.println("Alignment 4: " + alignment4);
}
public enum Alignment {
EVIL("Evil"),
NEUTRAL("Neutral"),
GOOD("Good");
private String name;
Alignment(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public static Alignment fromString(String name) {
for (Alignment alignment : Alignment.values()) {
if (alignment.name.equalsIgnoreCase(name)) {
return alignment;
}
}
throw new IllegalArgumentException("No alignment with name " + name + " found");
}
}
}
This outputs the following:
Alignment 1: GOOD
Alignment 2: NEUTRAL
Alignment 3: EVIL
Exception in thread "main" java.lang.IllegalArgumentException: No alignment with name unknown found
at AlignmentTest$Alignment.fromString(AlignmentTest.java:44)
at AlignmentTest.main(AlignmentTest.java:19)
Create a mapping (static map) inside your enum for holding the map between the name to be provided by the user to the enum.
public enum Alignment {
Evil("Evil"),
Neutral("Neutral"),
Good("Good"),
Undefined("Undefined");
private static final Map<String, Alignment> MAPPINGS = new HashMap<>();
static {
for (Alignment alignment : Alignment.values()) {
MAPPINGS.put(alignment.getName(), alignment);
}
}
private String name;
Alignment(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Alignment getAlignmentForName(String name) {
return MAPPINGS.get(name);
}
Now, you can use getAlignmentForName to map the user input to an enum. It will return null for invalid values.
The advantage of this is that you need not change any code when you add a new enum instance... In your case, an appropriate switch case has to be added.
Note: The names of enum fields must be in uppercase letters as per the conventions.
I have an enum defined as below, which contains a static function fromString(String s) which is like valueOf(String s) but case insensitive.
enum Platform {
TWITTER("TWITTER"), INSTAGRAM("INSTAGRAM"), UNKNOWN;
private String value;
Platform(String value) {
this.value = value;
}
private static final Map<String, Platform> stringToEnumMap = new HashMap<>();
static {
for (Platform platform : values()) {
stringToEnumMap.put(platform.toString().toLowerCase(), platform);
}
}
public static Platform fromString(String symbol) {
Platform platform = stringToEnumMap.get(symbol.toLowerCase());
if (platform != null) {
return platform;
} else {
return UNKNOWN;
}
}
public String getValue() {
return value;
}
#Override
public String toString() {
return getValue();
}
}
but when I execute the following code
Platform platform = Platform.fromString("twitter");
I get Platform.UNKNOWN returned on few devices on production.
Any Idea?
Update
symbol.toString() gives twitter
stringToEnumMap.toString() gives this
Platform StringToEnumMap: {
twıtter=TWITTER, ınstagram=INSTAGRAM,
unknown=UNKNOWN}
If you look closely, the letter i is different in the keys of HashMap and thats why string comparison fails.
the hexvalue of letter ı in stringToEnumMap is 0131, whereas the it should be 0069
Why is this happening on only few devices? How to avoid it?
It seems to be a dirty ide issue, try to clean up your project, build and run again.
UPDATE
Try replacing your if statement by getOrDefault method:
public static Letter fromString(String symbol) {
return stringToEnumMap.getOrDefault(symbol.toLowerCase(), UNKNOWN);
}
In my Opinion, there is no need for a map.... nor a new method for doing something that valueOf() is able to do...
look this implemetation..
enum ECase {
A, B, UNK;
public static ECase resolveEnumFromString(final String string) {
ECase r = null;
try {
r = ECase.valueOf(string.toUpperCase());
} catch (final IllegalArgumentException e) {
r = ECase.UNK;
}
return r;
}
}
you can verify the results doing:
ECase d = null;
d = ECase.resolveEnumFromString("a");
System.out.println(d);
d = ECase.resolveEnumFromString("A");
System.out.println(d);
d = ECase.resolveEnumFromString("0");
System.out.println(d);
I am able to find what the issue was. After some careful logging I found the issue was this http://mattryall.net/blog/2009/02/the-infamous-turkish-locale-bug.
Apparently toLowerCase() and toUpperCase() functions are locale dependent due to which you can't use them safely for case insensitive string comparison.
So all you need to do is pass the english locale in the parameter of these functions like this -
toLowerCase(Locale.ENGLISH).
I have the following code :
class MyClass {
private Value value;
public enum Value {
INSERT_ONLY("INSERT_ONLY"), UPDATE_ONLY("UPDATE_ONLY"), UPSERT("UPSERT") ;
private final String val ;
private Value(final String v) {val = v ;}
public String toString() {return val ;}
public String getVal() {
return val;
}
} ;
public Value getValue() {
return value;
}
public void setValue(Value value) {
this.value = value;
}
}
public class one {
public static void main(String[] args) {
MyClass obj = new MyClass() ;
obj.setValue(MyClass.Value.INSERT_ONLY) ;
String s = obj.getValue().toString() ;
String s1 = MyClass.Value.INSERT_ONLY.toString() ;
switch(s) {
case "INSERT_ONLY" : System.out.println("INSERT_ONLY") ;
break ;
case "s2" : System.out.println("s2") ;
break ;
}
}
}
This code works. But what I want is that in switch-case I use the strings as defined in the enum Value. If I use s1 in case, it generates an error. What is the way out?
Enums have a method .name() that returns the enum as a String. All your "values" are equivalent to this - just switch on that.
You can delete the value field, the constructor and getter and call name() where you are currently calling getValue().
Further, the default implementation of toString() returns name() so you can delete your toString() method without having any effect.
Finally, you can switch on the enum itself.
you string in the enum is actually the same as the enumertor constants... so it is a little redundant and completely unnescessary...
try this:
enum Value {
INSERT_ONLY, UPDATE_ONLY, UPSERT;
}
public static void main(String[] args) {
Week obj = new Week() ;
Value s = obj.getValue( ) ;
switch(s) {
case INSERT_ONLY : System.out.println("INSERT_ONLY") ;
break ;
case UPDATE_ONLY : System.out.println("UPDATE_ONLY") ;
break ;
}
}
}
You could try it this way:
MyClass.Value value = obj.getValue() ;
switch(value) {
case INSERT_ONLY : System.out.println("INSERT_ONLY") ;
break ;
case UPSERT : System.out.println("Upsert") ;
break ;
case UPDATE_ONLY : System.out.println("Update only") ;
break ;
}
Basically, the switch-statement uses the enum-names. So there is no need to apply the toString()-method on the Value returned by obj.getValue().
If you would like to know why switch will not work in combination with strings, please have a look here.
One more suggestion: add also the default-branch to the switch-statement.
If I understand correctly, you're trying to look up an enum constant by an unknown string s. The reason your case expression can't be s1 is because it must be a compile-time constant, which s1 is not. Since your example seems to be mostly theoretical, I'll suggest a few options and you can pick the most appropriate for your actual case:
Assuming the enum names are the same as their values (in which case you can scrap the field entirely) and you're just trying to look up an enum by its name, just do this:
MyClass.Value v = MyClass.Value.valueOf(s);
This will throw an IllegalArgumentException if no mapping is found for s1.
Still assuming the names are the same, but you do need an actual switch with some additional cases and custom logic:
try {
MyClass.Value v = MyClass.Value.valueOf(s);
switch (v) {
case INSERT_ONLY : System.out.println("INSERT_ONLY") ;
break ;
}
} catch (IllegalArgumentException e) {
switch (s)
case "s2" : System.out.println("s2") ;
break ;
}
}
If the names are not actually the same, you can add a static map of constants inside the enum class, to simulate valueOf():
public enum Value {
ONLY_INSERT("ONLY_INSE"), ONLY_UPDATE("UPDATE_ONLY"), UPSERT("UPSERT") ;
private static final Map<String, Value> byName = new HashMap<>();
static {
for (Value v : values()) {
byName.put(v.getVal(), v);
}
}
public static Value byName(String name) {
Value result = byName.get(name);
if (result == null) {
throw new IllegalArgumentException("Invalid name" + name);
}
return result;
}
private final String val ;
private Value(final String v) {val = v ;}
public String toString() {return val ;}
public String getVal() {
return val;
}
} ;
Now you can do the same as the previous solutions, using MyClass.Value.byName().
I'd like to point out that I'm very new to Java, which is why I may be making stupid mistakes.
I have a class called "Characters", which consists of 4 variables and multiple methods. All variables are private, so from what I've read, I need to use methods to do anything to them.
One of the methods is supposed to return one of the variables in string form, however I keep getting an error from both eclipse and when I run it. "This method must return a result of type "String". The error occurs on the first line of the method:
public String displayStats(String option) {
switch (option) {
case "charName":
System.out.println(charName);
return charName;
case "charHealth":
System.out.println(charHealth);
String charHealth2 = Integer.toString(charHealth);
return charHealth2;
case "charMana":
System.out.println(charMana);
String charMana2 = Integer.toString(charMana);
return charMana2;
case "charStamina":
System.out.println(charStamina);
String charStamina2 = Integer.toString(charStamina);
return charStamina2;
default:
System.out.println("Error on default");}
}
}
The full class:
package basics;
public class Characters {
private String charName = "";
private int charHealth = 0;
private int charMana = 0;
private int charStamina = 0;
public void summoner(Characters player) {
player.charName = "Summoner";
player.charHealth = 80;
player.charMana = 150;
player.charStamina = 50;}
public void sentinel(Characters player) {
player.charName = "Sentinel";
player.charHealth = 200;
player.charMana = 50;
player.charStamina = 100;}
public void beserker(Characters player) {
player.charName = "Beserker";
player.charHealth = 100;
player.charMana = 0;
player.charStamina = 200;}
public void mage(Characters player) {
player.charName = "Mage";
player.charHealth = 80;
player.charMana = 200;
player.charStamina = 20;}
public String displayStats(String option) {
switch (option) {
case "charName":
System.out.println(charName);
return charName;
case "charHealth":
System.out.println(charHealth);
String charHealth2 = Integer.toString(charHealth);
return charHealth2;
case "charMana":
System.out.println(charMana);
String charMana2 = Integer.toString(charMana);
return charMana2;
case "charStamina":
System.out.println(charStamina);
String charStamina2 = Integer.toString(charStamina);
return charStamina2;
default:
System.out.println("Error on default");}
}
}
You aren't returning anything in the default case of your switch statement, which means that there is a possibility (however small) that the method won't know what to return.
In the displayStats function you don't return a String in all paths of your code.
This is because the default doesn't return at all.
Maybe you wanted to write:
default:
return "Error on default";
Two problems: charName is a string, but charHealth, charMana, and charStamina are ints. Thus, your displayStats function isn't always returning a string.
Also, your default option in your switch statement should return a string as well.
It would be better to create an accessor function for each variable:
public String getCharName() {
return charName;
}
public int getCharHealth() {
return charHealth;
}
etc.
The method displayStats must always return a String or throw an exception. Since the code implies that the default case is an error, then throw an exception. At this point rather than create a new class of exception, just throw an IllegalArgumentException -- new IllegalArgumentException(option). When printed out it will state the type of exception and the value of the invalid option.
I am trying to get input validation by comparing the result of a string (from Scanner) to an Enum of potential values.
The Enum contains the name of all Countries in the world, the user is asked to enter a country name -- the input should only be allowed IF the input value is present in the Enum -- is there a way to do this?
Thanks!
The most efficient way is to try to get Enum by name and catch the exception, using Enum.valueOf(String) method:
try {
CountryEnum country = CountryEnum.valueOf( "user-input" );
} catch ( IllegalArgumentException e ) {
System.err.println( "No such country" );
}
Another way without catching exceptions is to compare user input to each of enum values:
String userInput = ...;
boolean countryExists = false;
for( CountryEnum country : CountryEnum.values() ) {
if( userInput.equalsIgnoreCase( country.name() ) ) {
countryExists = true;
break;
}
}
if( !countryExists ) {
System.err.println( "No such country" );
// exit program here or throw some exception
}
Use Enum.valueOf("string") != null if the string value exists as a type enum
Find more reference here - http://www.tutorialspoint.com/java/lang/enum_valueof.htm
You can equip the Enum with a method getByName(String name) that returns null if the Enum holds no corresponding value for the given name:
public enum Country {
AFGHANISTAN,
ALBANIA,
ALGERIA,
...
public static Country getByName(String name) {
try {
return valueOf(name.toUpperCase());
} catch (IllegalArgumentException e) {
return null;
}
}
}
Now when a user enters 'Neverland', obviously getByName('Neverland') returns null, which you can test for. Instead of null you could also include a catch-all value in your list and return that, e.g. TERRAINCOGNITA.
Following should work
public class EnumTest {
enum Country{IND, AUS, USA;
static boolean exists(String key) {
Country[] countryArr = values();
boolean found = false;
for(Country country : countryArr) {
if(country.toString().equals(key)) {
found = true;
break;
}
}
return found;
}
};
public static void main(String[] args) {
System.out.println("Started");
Scanner scan = new Scanner(System.in);
System.out.println(Country.exists(scan.nextLine()));
scan.close();
}
}
Of course you can implement a more efficient search by using a Set to store values.
Enum.valueOf can not be used as it throws following exception when passed value does not match to any enum constant.
java.lang.IllegalArgumentException: No enum constant
public enum RegisterType {GUIDE, VISITOR, TICKET, RECEPTIONIEST;}
RegisterType type = RegisterType.valueOf(input.next());