I have read the question Difference of Enum between java and C++? but I'm still confused.
I would like the following to return the related String:
public enum Checker {
EMPTY ("Empty"),
RED ("Red"),
YELLOW ("Yellow");
}
From what I have read, this should be possible. Just would like you to shed some light on it on how to implement it.
Short Answer
You need a constructor, a field and a getter.
Constructors
Enum types can have constructors, provided that their access level is either private or default (package-private). You can not directly call these constructors, except in the enum declaration itself. Similar to classes, when you define an enum constant without parameters, you actually call the default constructor generated by the compiler. E.g.
public enum King {
ELVIS
}
is equivalent to
public enum King {
ELVIS() // the compiler will happily accept this
}
And just like in classes, if you define an explicit constructor, the compiler will not insert a default constructor, so this will not compile:
public enum King {
ELVIS, // error, default constructor is not defined
MICHAEL_JACKSON(true)
;
private boolean kingOfPop;
King(boolean kingOfPop){this.kingOfPop = kingOfPop;}
}
This is a pretty good reference on enums that also explains the constructor issues.
Fields and Accessors
Enums are constants and are immutable as such. They can however define fields, that can have state. This is an awful practice, because developers will expect enums and their associated values to be constants, but you can still define a non-final field in an enum with getters and setters.
This is legal java code:
public enum Color {
RED("FF0000"),
GREEN("00FF00"),
BLUE("0000FF");
private String code;
public String getCode(){return code;}
public void setCode(String code){this.code = code;}
private Color(String code){this.code = code;}
}
But it enables evil code like this:
String oldBlue = Color.BLUE.getCode();
Color.BLUE.setCode(Color.RED.getCode());
Color.RED.setCode(oldBlue);
So in 99.99 % of cases: if you have fields in your enums, you should make them final and provide getters only. If the fields are not immutable themselves, provide defensive copies:
public enum Band {
THE_BEATLES("John","Paul","George","Ringo");
private final List<String> members;
public List<String> getMembers(){
// defensive copy, because the original list is mutable
return new ArrayList<String>(members);
}
private Band(String... members){
this.members=Arrays.asList(members);
}
}
Solution
In your case it's very simple: you just need a single field of type string (immutable), so initializing it in the constructor and providing a getter is perfectly ok:
public enum Checker {
EMPTY ("Empty"),
RED ("Red"),
YELLOW ("Yellow");
private final String value;
private Checker(final String value) {
this.value = value;
}
public String getValue() { return value; }
}
If the pattern holds, this works as well and eliminates the repetition:
public enum Checker {
EMPTY,
RED,
YELLOW;
public String getDescription(){
String name = name();
return ""+Character.toUpperCase(name.charAt(0))
+name.substring(1).toLowerCase();
}
}
Related
I have this model of an object called expenseType:
public class ExpenseType {
private String gridText;
public enum type {
FOOD(Constants.EXPENSE_TYPE_FOOD, R.drawable.food_blue, R.drawable.food),
FLOWERS(Constants.EXPENSE_TYPE_FLOWERS, R.drawable.flowers_blue, R.drawable.flowers),
GROCERIES(Constants.EXPENSE_TYPE_GROCERIES, R.drawable.groceries_blue, R.drawable.groceries),
HOLIDAY(Constants.EXPENSE_TYPE_HOLIDAY, R.drawable.holiday_blue, R.drawable.holiday),
PHARMACY(Constants.EXPENSE_TYPE_PHARMACY, R.drawable.pharmacy_blue, R.drawable.pharmacy),
BILLS(Constants.EXPENSE_TYPE_BILLS, R.drawable.bills_blue, R.drawable.bills),
CLOTHES(Constants.EXPENSE_TYPE_CLOTHES, R.drawable.clothes_blue, R.drawable.clothes),
TRANSPORT(Constants.EXPENSE_TYPE_TRANSPORT, R.drawable.transport_blue, R.drawable.transport),
ITEMS(Constants.EXPENSE_TYPE_ITEMS, R.drawable.items_blue, R.drawable.items),
OTHERS(Constants.EXPENSE_TYPE_OTHERS, R.drawable.others_blue, R.drawable.others);
private String expenseKey;
private int drawableBlue, drawableWhite;
type(String expenseKey, #DrawableRes int drawableBlue, #DrawableRes int drawableWhite) {
this.expenseKey = expenseKey;
this.drawableBlue = drawableBlue;
this.drawableWhite = drawableWhite;
}
public String getKey() {
return expenseKey;
}
public int getDrawableBlue() {
return drawableBlue;
}
public int getDrawableWhite() {
return drawableWhite;
}
}
public ExpenseType(String gridText) {
this.gridText = gridText;
}
public String getGridText() {
return gridText;
}
public void setGridText(String gridText) {
this.gridText = gridText;
}
}
The string gridText gets written inside a database, but I do not want to add the drawable values to the database as well, so I created that enum that has the drawable variation. Now, inside a recycle view adapter, how can I access the getDrawableBlue() from the enum so I can set an icon correspondent to my expenseType?
I have this code in the adapter:
private void checkSelect(ExpenseType expenseType) {
if(positionSelected == getAdapterPosition()){
gridIcon.setImageResource(????????);
return;
}
How can I access that getter instead of the ???????? so I can get my drawable value stored in that enum?
This looks like a glitch in your class design.
Your enum type is a nested class in your ExpenseType class.
(Note: as it's an enum, it's implicitly a static nested type).
In order for you to be able to invoke the accessors of a type, you will need to reference its specific type somehow.
One way to do so would be to have a type as instance field of ExpenseType, pretty much like the String gridText.
You would then need to bind a specific type type to your ExpenseType instance (I know, this gets semantically confusing but I didn't name your variables :).
In other words, each instance of ExpenseType has its own type field assigned with one type of... errr.. type.
So instance 1 of ExpenseType has a FOOD value for its type, instance 2 of ExpenseType has a FLOWERS value for its type, etc.
You could then add a getter and reference the drawableBlue int in checkSelect by invoking:
// assuming field type can't be null
expenseType.getType().getDrawableBlue()
expenseType seems to be of the class type ExpenseType but the enum you're refering to is the inner enum type (which should be renamed btw). Thus you'd need to either provide a field for type in ExpenseType and a getter for that and then call something like expenseType.getType().getDrawableBlue() or refactor your code (depends on what ExpenseType is meant to represent).
As for the renaming: the class ExpenseType has a field gridText which might indicate that it actually represents a cell. If that's the case I'd suggest doing something like this:
public class ExpenseGridCell {
private String gridText;
private ExpenseType type; //that would be your enum tyoe
public ExpenseType getType() {
return type;
}
}
//I'll move the enum to a separate class which makes it easier to use elsewhere (the outer class would be a "namespace" only anyway)
public enum ExpenseType {
FOOD(Constants.EXPENSE_TYPE_FOOD,R.drawable.food_blue, R.drawable.food),
...;
}
If gridText isn't a cell's text but the same for every type then you might want to merge the inner enum type into ExpenseType, i.e. something like this:
public enum ExpenseType {
FOOD(Constants.EXPENSE_TYPE_FOOD, "Some grid text for food", R.drawable.food_blue, R.drawable.food),
...;
private String gridText;
private String expenseKey;
private int drawableBlue, drawableWhite;
//appropriate constructor and getters
}
Some background on the project: I am attempting to craft a space/sci-fi combat sim game with tabletop rpg style dice mechanics cranked up to 11 on the complexity scale, but still being transparent about the die rolls going on under the hood. I'm currently using the Star Wars Saga Edition combat rules as a basis.
Currently I'm trying to figure out a way to assign traits to vehicle.(possibly stored as a class for each vehicle) Each trait is an enum so that it can store multiple pieces of information. Here is the code I have for size categories:
public enum VehicleSize {
LARGE(1,"Speeder bike",5),HUGE(2,"Small Fighter",10),GARGANTUAN(5,"Tank, Medium Fighter",20),COLOSSAL(10,"Imperial Walker, Light Freighter",50),
COLLOSSALfrigate(10,"Corvette, Frigate",100),COLLOSSALcruiser(10,"Imperial-class Star Destroyer, Cruiser",200),
COLLOSSALstation(10,"The Death Star, Space Station",500);
private final int refMod;
private final int threshMod;
private final String desc;
VehicleSize(int reflexModifier,String example,int thresholdModifier)
{
refMod = reflexModifier;
desc = example;
threshMod = thresholdModifier;
}
public int getRefMod() {
return refMod;
}
public String getDesc() {
return desc;
}
public int getThreshMod() {
return threshMod;
}
}
My question is such: How do create vehicle profiles in such a way that I can assign this and similar enums as traits?
For practically all purposes, a field whose type is an enum class is no different than a field of any other object type, like Integer or String.
Create a private field, add a getter and setter, or if the field is final (likely in your case, because a vehicle instance can't change its type), add it as a constructor parameter and remo e the setter.
public class Vehicle {
private final VehicleSize vehicleSize;
// other fields
public Vehicle(VehicleSize vehicleSize) {
this.vehicleSize = vehicleSize;
}
public VehicleSize getVehicleSize() {
return vehicleSize;
}
// rest of class
}
There is nothing mysterious about an enum, other than the number of different instances of it are known at compile time (and a few more things, but nothing scary).
To add this into a class, you can use it like any user defined type.
public class MyClass {
private MyEnum myEnum;
}
Please have a look at Synthetic Arguments. Enum constructors have two additional synthetic arguments.
Please look at the section:
Another example: Java enum classes
As you can see, it saves quite some code, but also adds synthetic fields, methods and constructor parameters. If you had defined your own constructor, with its own set of parameters.
Can there be a situation where a enum constructor does not have any synthetic arguments.
Apologies for not providing enough detail.
Having read the article, I would say the answer is no. The article explains that a typical enum such as:
enum Colours {
RED, BLUE;
}
Becomes:
final class Colours extends java.lang.Enum {
public final static Colours RED = new Colours("RED", 0);
public final static Colours BLUE = new Colours("BLUE", 1);
private final static values = new Colours[]{ RED, BLUE };
private Colours(String name, int sequence){
super(name, sequence);
}
public static Colours[] values(){
return values;
}
public static Colours valueOf(String name){
return (Colours)java.lang.Enum.valueOf(Colours.class, name);
}
}
where the arguments to the Colours constructor are considered synthetic (i.e. they've been produced by the compiler to make sure "stuff works"). So it seems the synthetic arguments are unavoidable as they're a necessary part of translating an enum into a real class.
The only possibility is if the enum has no values - does Java still create the synthetic fields? Intuitively, the answer is yes. This is backed up by the article in the OK, but why should I care? section. Here the author shows that an empty enum still has a parameter count of two, when viewed with reflection.
Check the source code of the Concurrent class of TimeUnit. It's an enum with its own methods.
You can have work with enums like if they were class themselves.
http://fuseyism.com/classpath/doc/java/util/concurrent/TimeUnit-source.html
Here is an example of mine:
public enum ExampleEnum {
ENUM_1 ( "ENUM_1", 1, Color.GREEN ) {
#Override
public void doMethingWeird( String stringToEnum ) {
//Implementation goes here;
}
},
ENUM_2 ( "ENUM_2", 2, Color.BLACK ) {
#Override
public void doMethingWeird( String stringToEnum ) {
//Implementation goes here;
}
},
ENUM_3 ( "ENUM_3", 3, Color.WHITE ){
#Override
public void doMethingWeird( String stringToEnum ) {
//Implementation goes here;
}
}; //Don't forget the semicolon ';' after the enums, to separate them from the methods;
//You can have static constants;
private static final Object object = new Object();
private final String enumName;
private final int enumNumber;
private final Color enumColor; //why not?
//CONSTRUCTOR IT MUST BE PRIVATE
private Effect( String enumName, int enumNumber, Color enumColor ){
this.enumName = enumName;
this.enumNumber = enumNumber;
this.enumColor = enumColor;
}
//you can have abstract methods and implement them on the enums.
public abstract void public void doMethingWeird( String stringToEnum );
public String getEnumName() {
return enuName;
}
public int getEnumNumber() {
return enumNumber;
}
public Color getEnumColor() {
return enumColor;
}
}
I hope I have helped.
I was running into the same problem, having an enum with tree constructors and parameters. Doing reflection and getting the constructors parameters, you get a String and an int extra as the first 2 parameters. I was wondering where they were coming from. After debugging I found out that the Enum class has a protected constructor, which is using the first 2 parameters.
I did a test by adding a constructor without parameters, and the 2 extra parameters are added too.
Code from Enum.java with the constructor:
/**
* Sole constructor. Programmers cannot invoke this constructor.
* It is for use by code emitted by the compiler in response to
* enum type declarations.
*
* #param name - The name of this enum constant, which is the identifier
* used to declare it.
* #param ordinal - The ordinal of this enumeration constant (its position
* in the enum declaration, where the initial constant is assigned
* an ordinal of zero).
*/
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
To detect this situation, you can use the isEnum() method of the reflected constructor, and skip the 2 parameters.
Constructor<?> constructor;
private boolean isEnum() {
return constructor.getDeclaringClass().isEnum();
}
So these 2 files I am going to post are each in my DataBase package. The DBBinding class just needs to create an object with a string for the key and one for the value. Then the DBrecord is going to keep a collection of DBBindings that all have the same key string but diffrent value strings. For some reason I can not think/find the correct way to make a add method in DBrecord so that it calls the DBBinding class/objects constructor.
This is the method that is supposed to add the binding:
private void addRecord(String key_, String value_)
{
//DBBinding myDBBinding=new DBBinding(key_, value_);//constructor not defined error
//DBBinding myDBBinding(key_,value_);
//DataBase.DBBinding myDBBinding=new DataBase.DBBinding(key_, value_);//constructor not defined error
}
Here's the DBBinding code followed by the DBrecord code.
package DataBase;
public class DBBinding {
private String key;
private String value;
public void DBBinding(String key_, String value_)
{
String key =new String(key_);
String value=new String(value_);
}
//public String toString()
//{return key+": "+value;}
}
and
package DataBase;
//package DataBase.*;
import DataBase.*;//did not help ... ?
public class DBrecord {
boolean select;
String key;
//need some type of collection to keep bindings.
public void DBrecord()
{
DBrecord myRecord=new DBrecord();
select=false;
}
private void addRecord(String key_, String value_)
{
//DBBinding myDBBinding=new DBBinding(key_, value_);//constructor not defined error
//DBBinding myDBBinding(key_,value_);
//DataBase.DBBinding myDBBinding=new DataBase.DBBinding(key_, value_);//constructor not defined error
}
public String toString()
{
//out put key first then all values in collection/group/record. use correct formatting.
}
}
In class DBBinding you must have public DBBinding(String key_, String value_), the void makes the "constructor" actually to a method :-)
You have the same error in DBrecord.
By the way, don't do this:
String key =new String(key_);
Strings are immutable, nothing can happen if you "share" them. But your code forces Java to create a new object for an absolutely identical value. So use just
String key = key_;
However, in your case even this is wrong, as you create a new local variable key "shadowing" the class variable key. Look here for an explanation.
So alltogether DBBinding should look like:
package DataBase;
public class DBBinding {
private String key;
private String value;
public DBBinding(String key_, String value_)
{
key = key_;
value = value_;
}
...
}
If you want your code to work please remove the void construct from your constructors.
Signature differences
Constructors and methods differ in three aspects of the signature: modifiers, return type, and name. Like methods, constructors can have any of the access modifiers: public, protected, private, or none (often called package or friendly). Unlike methods, constructors can take only access modifiers. Therefore, constructors cannot be abstract, final, native, static, or synchronized.
The return types are very different too. Methods can have any valid
return type, or no return type, in which case the return type is given
as void. Constructors have no return type, not even void.
I have read the question Difference of Enum between java and C++? but I'm still confused.
I would like the following to return the related String:
public enum Checker {
EMPTY ("Empty"),
RED ("Red"),
YELLOW ("Yellow");
}
From what I have read, this should be possible. Just would like you to shed some light on it on how to implement it.
Short Answer
You need a constructor, a field and a getter.
Constructors
Enum types can have constructors, provided that their access level is either private or default (package-private). You can not directly call these constructors, except in the enum declaration itself. Similar to classes, when you define an enum constant without parameters, you actually call the default constructor generated by the compiler. E.g.
public enum King {
ELVIS
}
is equivalent to
public enum King {
ELVIS() // the compiler will happily accept this
}
And just like in classes, if you define an explicit constructor, the compiler will not insert a default constructor, so this will not compile:
public enum King {
ELVIS, // error, default constructor is not defined
MICHAEL_JACKSON(true)
;
private boolean kingOfPop;
King(boolean kingOfPop){this.kingOfPop = kingOfPop;}
}
This is a pretty good reference on enums that also explains the constructor issues.
Fields and Accessors
Enums are constants and are immutable as such. They can however define fields, that can have state. This is an awful practice, because developers will expect enums and their associated values to be constants, but you can still define a non-final field in an enum with getters and setters.
This is legal java code:
public enum Color {
RED("FF0000"),
GREEN("00FF00"),
BLUE("0000FF");
private String code;
public String getCode(){return code;}
public void setCode(String code){this.code = code;}
private Color(String code){this.code = code;}
}
But it enables evil code like this:
String oldBlue = Color.BLUE.getCode();
Color.BLUE.setCode(Color.RED.getCode());
Color.RED.setCode(oldBlue);
So in 99.99 % of cases: if you have fields in your enums, you should make them final and provide getters only. If the fields are not immutable themselves, provide defensive copies:
public enum Band {
THE_BEATLES("John","Paul","George","Ringo");
private final List<String> members;
public List<String> getMembers(){
// defensive copy, because the original list is mutable
return new ArrayList<String>(members);
}
private Band(String... members){
this.members=Arrays.asList(members);
}
}
Solution
In your case it's very simple: you just need a single field of type string (immutable), so initializing it in the constructor and providing a getter is perfectly ok:
public enum Checker {
EMPTY ("Empty"),
RED ("Red"),
YELLOW ("Yellow");
private final String value;
private Checker(final String value) {
this.value = value;
}
public String getValue() { return value; }
}
If the pattern holds, this works as well and eliminates the repetition:
public enum Checker {
EMPTY,
RED,
YELLOW;
public String getDescription(){
String name = name();
return ""+Character.toUpperCase(name.charAt(0))
+name.substring(1).toLowerCase();
}
}