I want to define a default value for an Enum class. The idea of my enum is to define a few specific String values and tie them to enumerations. However, if a user provide an String that I am not expecting, I want the enum to reflect an invalid state. Consider
public class EnumDemo {
public enum Food {
HAMBURGER("h"), FRIES("f"), HOTDOG("d"), ARTICHOKE("a"), INVALID("invalid");
Food(String code) {
this.code = code;
}
private final String code;
public String getCode() {
return code;
}
public static Food fromString(String value) {
return Arrays.stream(Food.values()).filter(s -> s.code.equalsIgnoreCase(value)).findFirst()
.orElse(Food.INVALID);
}
}
public static void main(String[] args) {
Food f1 = Food.fromString("h");
System.out.println(f1 + " " + f1.getCode());
f1 = Food.fromString("x");
System.out.println(f1 + " " + f1.getCode());
}
}
this prints out
HAMBURGER h
INVALID invalid
The problem here is that I am defining the string for the code. invalid is hardcoded as per
INVALID("invalid")
Is it possible to make this a variable? So that I can keep track of what the invalid input was? I tried
INVALID(String x)
but obviously got a syntax exception. Would it just be better not to use an enum?
Lastly, the reason I want to keep track of invalid inputs is that in the future, I want the flexibility to change the enum depending on the users.
You could create a wrapper to stash the original input:
public class FoodInput {
private final Food food;
private final String input;
public FoodInput(String input) {
this.food = Food.fromString(input);
this.input = input;
}
public Food getFood() {
return food;
}
public String getInput() {
return input;
}
}
Related
How is the method getAbbreviation() in an Enum class implemented?
The following code is from the book Core Java I.
public class EnumTest {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Enter a size: (SMALL, MEDIUM, LARGE, EXTRA_LARGE) ");
String input = in.next().toUpperCase();
Size size = Enum.valueOf(Size.class, input);
System.out.println("size=" + size);
System.out.println("abbreviation=" + size.getAbbreviation());
if (size == Size.EXTRA_LARGE) {
System.out.println("Good job--you paid attention to the _.");
}
}
}
enum Size {
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
private Size(String abbreviation) {
this.abbreviation = abbreviation;
}
public String getAbbreviation() {
return abbreviation;
}
private String abbreviation;
}
The program execution picture is shown here.
My question is: why it can output abbreviation=S, how is it implemented internally?
It is an invocation of the enum constructor is an invocation of the enum constructor Size(String abbreviation).
Actually,the constructor of Enum class is private , programmer can not use it.So the provided code offered in the book
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
is functionally the same to :
class Size{
private String abbreviation;
private Size(String abbreviation){this.abbreviation=abbreviation;}
public String getAbbreviation(){return abbreviation;}
public static Size SMALL = new Size("S");
public static Size MEDIUM = new Size("M");
public static Size LARGE = new Size("L");
public static Size EXTRA_LARGE = new Size("XL");
}
although i haven't found the source code of this implemention process,but I think it goes something like this.
Say I have an enum of Things with names:
public enum Thing {
THINGA, THINGB, THINGC, THINGD,
THINGE("Thing E"),
;
public final String name;
private Thing(String pname) {
name = pname;
//...
}
private Thing() {
// See question
}
}
Say I want to do the same thing I did with THINGE for all my other Things, except I want the default name to be the same as that of the enum constant itself, e.g.:
private Thing() {
this(/* the name of the constant itself */);
}
Essentially, I want the implied value of pname to be "THINGA" for THINGA, "THINGB" for THINGB, "THINGC" for THINGC, "THINGD" for THINGD, etc. Using either toString() or name(), the compiler yells:
error: cannot reference this before supertype constructor has been called
Is there any way to avoid this?
Specifically, I include the names for the sake of the user. It feels more natural to look at "Thing E" rather than "THINGE", but its much easier for me to program if I can write a method to turn "THINGA" into "Thing A" and use that in the constructor.
You don't need to give a parameter to the constructor for this, using name() and a bit of String manipulation should get you there.
public static void main(String[] args) {
for (Thing value : Thing.values()) {
System.out.println("value.name = " + value.name);
}
}
public enum Thing {
THINGA,
THINGB,
THINGC,
THINGD,
THINGE;
public final String name;
Thing() {
this.name = name().replace("THING", "Thing ");
}
}
Prints
value.name = Thing A
value.name = Thing B
value.name = Thing C
value.name = Thing D
value.name = Thing E
I believe from your question that you don't want to change what .name() returns, but introduce another field on your enum values which defaults to .name().
You can do this:
public class Comparison
{
public enum Foo {
A, B, C("My C");
private final String myName;
Foo(String myName) {
this.myName = myName;
}
Foo() {
myName = this.name();
}
public String getMyName() {
return myName;
}
}
public static void main(String[] args)
{
System.out.println(Arrays.stream(Foo.values()).map(e -> e.getMyName()).collect(Collectors.joining("\n")));
}
}
I create enum which has two values: brand name and brand code.
I want to know the brand code by inputting the brand name.
And I also want to know the brand name by inputting the brand code.
Can this problem solved using Enum? or other code is more effective? I want to make the code as shorter as possible
I have created following code to search the code of a brand. If I want to do vice versa, I can create another Hashmap and method to convert the code into a brand. But is that the effective way to solve it?
public enum Brand{
COLA("cola", "CL8935"),
BREAD("bread", "BR2810"),
SNICKERS("snickers", "SN4423");
private static final Map<String, String> BY_BRAND = new HashMap<>();
static {
for (Brand brand : values()){
BY_BRAND.put(brand.code, brand.brand);
}
}
private final String brand;
private final String code;
public static String convertToCode(String brand){
return BY_BRAND.get(brand.toLowerCase()).toString();
}
}
Update - Adding the full enum (with imports)
import java.util.Arrays;
import java.util.function.Function;
enum Brand {
COLA("cola", "CL8935"),
BREAD("bread", "BR2810"),
SNICKERS("snickers", "SN4423");
private final String brand;
private final String code;
Brand(String brand, String code) {
this.brand = brand;
this.code = code;
}
public static Brand findBy(String value, Function<Brand, String> extractor) {
return Arrays.stream(Brand.values())
.filter(brand -> extractor.apply(brand).equalsIgnoreCase(value))
.findFirst()
.orElse("Either a default or throw exception here");
}
public String getBrand() {
return brand;
}
public String getCode() {
return code;
}
}
Original
You could use a static findBy method as an alternative to the map. This would allow you to pass in the value and method reference for the getter which will be used to compare the values stored within the enum.
The difference here would be performance (as maps would be faster), the fact that you would be returning the enum and that you most likely would want either a default enum value or to throw an exception on no matched being found. Below is an example
public static Brand findBy(String value, Function<Brand, String> extractor) {
return Arrays.stream(Brand.values())
.filter(brand -> extractor.apply(brand).equalsIgnoreCase(value))
.findFirst()
.orElse("Either a default or throw exception here");
}
And this can be called like this
public static void main(String[] args) {
Brand brand1 = Brand.findBy("cola", Brand::getBrand);
Brand brand2 = Brand.findBy("BR2810", Brand::getCode);
}
Simple static method in Brand should do:
public static String getBrand(String code) {
for(Brand b : Brand.values()){
if(b.getCode().equals(code)) return b.getBrand();
}
return null;
}
Similarly you can write a getCode(String brand)
Edit: assuming the two attributes do not have the same value, you can check do the bi-di mapping in the same method:
public static String getOtherAttribute(String value) {
for(Brand b : Brand.values()){
if(b.getCode().equals(value)) return b.getBrand();
if(b.getBrand().equals(value)) return b.getCode();
}
return null;
}
If the two attributes may have the same value you can add an argument (flag) to the method's signature to tell which attribute you want to retrieve.
This question already has answers here:
How to retrieve Enum name using the id?
(11 answers)
Closed 9 years ago.
I need to do look up in an enum by an int . The enum is as folows :
public enum ErrorCode{
MissingReturn(1,"Some long String here"),
InvalidArgument(2,"Another long String here");
private final int shortCode ;
private final String detailMessage;
ErrorCode(shortCode ,detailMessage){
this.shortCode = shortCode ;
this.detailMessage= detailMessage;
}
public String getDetailedMessage(){
return this.detailMessage;
}
public int getShortCode(){
return this.shortCode ;
}
}
Now Is need to have a lookup method that would take an int code and should return me the String message pertaining to that code that is stored in the Enum.Passing a "1" should return me the String "Some long String here". What is the best way to implement this functionality?
public static String lookUpMessageFromCode(int code){
}
P.S: Is the class EnumMap useful for this kind of use case? If yes,please let me know why?
Depending on the int values that you associated with your enum, I would add a static array of ErrorCodes, or a static Map<Integer,ErrorCode> to your enum class, and use it to do a lookup in the message from code method. In your case, an array is more appropriate, because you have values 1 and 2 which are small. I would also change the signature to return ErrorCode.
private static final ErrorCode[] allErrorCodes = new ErrorCode[] {
null, MissingReturn, InvalidArgument
};
public static ErrorCode lookUpByCode(int code) {
// Add range checking to see if the code is valid
return allErrorCodes[code];
}
The callers who need the message would obtain it like this:
String message = ErrorCode.lookUpByCode(myErrorCode).getDetailedMessage();
I would simply iterate through your Enum values and check the code. This solution lets you utilize the existing Enum with out creating another object to manage.
public enum ErrorCode {
MissingReturn(1, "Some long String here"),
InvalidArgument(2, "Another long String here");
private final int shortCode;
private final String detailMessage;
ErrorCode(int shortCode, String detailMessage) {
this.shortCode = shortCode;
this.detailMessage = detailMessage;
}
public String getDetailedMessage() {
return this.detailMessage;
}
public int getShortCode() {
return this.shortCode;
}
public static String lookUpMessageFromCode(int code) {
String message = null;
for (ErrorCode errorCode : ErrorCode.values()) {
if (errorCode.getShortCode() == code) {
message = errorCode.getDetailedMessage();
break;
}
}
return message;
}
public static void main(String[] args) {
System.out.println(ErrorCode.lookUpMessageFromCode(1));
System.out.println(ErrorCode.lookUpMessageFromCode(2));
}
}
One thing to note
The Enum constructor is missing the type information regarding its parameters.
ErrorCode(int shortCode, String detailMessage) {
this.shortCode = shortCode;
this.detailMessage = detailMessage;
}
Here is another option:
public static String lookUpMessageFromCode(int code){
for(ErrorCode ec:ErrorCode.values()){
if(ec.shortCode==code)
return ec.detailMessage;
}
return null;
}
If I have a Object
public class Genre {
private int id;
private int name;
}
And the id and name were been determined in advance, for example
if (id == 1)
name = "action";
else if (id == 2)
name = "horror";
My problem is how to create these two methods well
Genre.getName(1); // return "action";
Genre.getId("action"); // return 1;
I thought maybe I can use enum, like
public enum Genre {
ACTION(1), HORROR(2);
private final int id;
private final String name;
private Genre(int id) {
this.id = id;
this.name = getName(id);
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public static String getName(int i) {
switch(i) {
case 1 : return "action";
case 2: return "horror";
default :
return null;
}
}
}
But in this way, I have no idea how to
Genre.getId("action"); // return 1;
And im afraid i use enum not correctly.
Could you give me some advice? Thanks!
---
At first, What I want to do this is in my case i want to use id or name to find the name or id like
int id = 1;
Genre.getName(id); // return "action"
or
String name = "action";
Genre.getId(name); // return 1
And now thanks for all the advices, I realize why I want to do is
int id = 1;
Genre.getGenre(id); // return Genre that id = 1 and the name = "action"
or
String name = "action";
Genre.getGenre(name); // return Genre that id = 1 and the name = "action"
If you insist on using an enum for this, you can just use the existing enum facilities. The solution below assumes the enum name and ordinal may be used in place of your name and id fields:
public enum Genre {
// ordinal 0, name = "ACTION"
ACTION,
// ordinal 1, name = "HORROR"
HORROR;
}
public static void main(String[] args) {
int horrorOrdinal = 1;
Genre horrorGenre = Genre.values()[horrorOrdinal];
String horrorName = horrorGenre.name();
String actionName = "ACTION";
Genre actionGenre = Genre.valueOf(actionName);
int actionOrdinal = actionGenre.ordinal();
System.out.println(String.format("%s=%s %s=%s", horrorName, horrorOrdinal, actionName, actionOrdinal));
}
Output:
HORROR=1 ACTION=0
Another suitable way would be to use a map for the lookup, like Michał Šrajer suggested:
private static Map<Integer, String> genres = new HashMap<Integer, String>();
public static void main(String[] args) {
initGenres();
int horrorOrdinal = 2;
String horrorName = genres.get(horrorOrdinal);
String actionName = "action";
int actionOrdinal = getGenreIdByName(actionName);
System.out.println(String.format("%s=%s %s=%s", horrorName, horrorOrdinal, actionName, actionOrdinal));
}
private static void initGenres() {
genres.put(1, "action");
genres.put(2, "horror");
}
private static int getGenreIdByName(String genreName) {
for (Entry<Integer, String> entry : genres.entrySet()) {
if (entry.getValue().equals(genreName)) {
return entry.getKey();
}
}
throw new IllegalArgumentException("Genre not found: " + genreName);
}
Output:
horror=2 action=1
Design considerations:
In this example I chose to use the (fast) map lookup for id->name and wrote a seperate method (getGenreIdByName) to do the reverse lookup name->id. You could reverse that, or use a second map to make both lookups fast (at the cost of needing to maintain an extra map).
I chose to store the id and name in the map. You could also use the Genre class itself as the map value. This would allow you to easily add extra fields (like 'description') later on.
If you need to represent you genres in different languages, you can use ResourceBundles to localize the output. Create a language file in your classpath root.
In file genres_nl.properties:
horror=heel eng
action=actie
Where the _nl suffix in the filename indicates the language.
Then in your code, in initGenres:
ResourceBundle genreNames = ResourceBundle.getBundle("genres", new Locale("nl");
And when getting the genre name:
String horrorName = genreNames.getString(genres.get(horrorOrdinal));
Note that getString can throw the runtime exception MissingResourceException if the bundle is not found. To avoid this, make sure you create a 'default' bundle with no suffix (so in this case a file named 'genres.properties') which is automatically used in case no bundle for the used Locale can be found.
Try the valueOf(...) method:
void String getId(String name) {
//names are upper case, so account for that
//handling non-existent names is an excersize for you
valueOf(name.toUpperCase()).getId();
}
Note that there are better methods (like Thilo suggested), but if you have a string only, you might use that.
Edit: another note:
In your getName(int i) method, you might want to return ACTION.name() etc. in order to be more refactoring safe and use the correct case.
You can get its ID by calling Genre.ACTION.getId();
This should do it:
Genre.ACTION.getId()
And if you need to do it at run-time:
Genre.valueOf("ACTION").getId()
ACTION(1, "action"), HORROR(2, "horror");
is a easy way to do it.
But if you are require to do it more often i would suggest you to create your own class and use MAP<-"-,-"-> as micheal said.
Edit:----
As you said the rarely gonna change use this way-->
public enum Genre {
ACTION(0, "action"), HORROR(1, "horror"), ROMANCE(2, "romance"), COMEDY(5, "comedy");
public final int id;
public final String name;
private Genre(int id, String name) {
this.id = id;
this.name = name;
};
public final static int length = Genre.values().length;
public static String[] getGenre() {
String[] genreList = new String[length];
int i = 0;
for (Genre attribute : Genre.values()) {
genreList[i++] = attribute.toString();
}
return genreList;
}
#Override
public String toString() {
return this.name;
}
}
Please remember use this as Genre.HORROR.id
also note that using this way is best as per your requirement.
Why don't you use the Enum Constructor with id and String:
public enum Genre {
ACTION(1, "action"), HORROR(2, "horror");
}
public enum Genre {
ACTION(1, "action"), HORROR(2, "horror");
private final int id;
private final String name;
private Genre(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
If you need to access particular element by it's name, you need to do it this way:
Genre.valueOf("ACTION").getId()
However, if you need to do it often, and in more dynamic way, I suggest to create regular class, and to keep all data in some Map<String, Movie> container.