I have some variables in an enum that for some reason have to start with '#', for example:
public enum PR304MainDb {
#MODE,
#USERID,
#ROLES,
#MAX_DOC_COUNT
}
The way I use these variables is to put them in a HashMap...:
Map<String, Object> in = new HashMap<String, Object>();
in.put(PR304MainDb.#MODE.toString(), 5);
...and then use the HashMap as parameters when calling a store procedure (which code I can't change).
Also after the call I read the results and do some comparisons, like:
if (out.getKey().equals(PR304MainDb.#MAX_DOC_COUNT.toString())) {
//DO SOMETHING
}
I know that these names are invalid, but is there any alternative way to accomplish this?
Yes there is a way - don't use the enum constant name (don't use YOUR_CONSTANT.toString() as the name).
Enums, like other classes, can have fields and methods. Some possibilities:
public enum PR304MainDb_Possibility1 {
MODE("#MODE"),
USERID("#USERID"),
ROLES("#ROLES"),
MAX_DOC_COUNT("#MAX_DOC_COUNT");
private PR304MainDb_Possibility1(String text) {
this.text = text;
}
public final String text;
}
public enum PR304MainDb_Possibility2 {
MODE,
USERID,
ROLES,
MAX_DOC_COUNT;
public String getText() {return "#" + name();}
}
You can't use # in the symbol name, but what you can do is over-ride the toString method:
public enum PR304MainDb {
MODE,
USERID,
ROLES,
MAX_DOC_COUNT;
#override
String toString() {
return "#"+super.toString();
}
}
You can also do this not using the toString() method at all by defining a new method for the purpose, but that would involve changing the code that is using the enum.
Related
I have a class that may have several enums within it.
Each enum is supposed to have a string value associated with each entry.
In order to achieve this, I have added a parametrized constructor,a supporting string class member and overridern the toString method.
However one can see that 50% of the code between my two enums are same. It's just the code to support mapping strings to the Enum values.
How can I move this code to a common place and avoid code duplication?
Edit: Use case is to easily obtain "New York" when I write America.STATES.NY.
Here's what I tried,
1) I tried using a common interface, but the constructors are different.
2) I tried using inheritance, but enums cannot be inherited
public class America {
public enum STATES {
NY("New York"), CA("California");
String displayName;
STATES(String displayName) {
this.displayName = displayName;
}
#Override
public String toString() {
return this.displayName;
}
}
public enum PROVINCES {
ON("Ontario"), QU("Qubec");
String displayName;
PROVINCES(String displayName) {
this.displayName = displayName;
}
#Override
public String toString() {
return this.displayName;
}
}
}
A typical phrase I like to use is "data isn't a part of behavior, and doesn't belong in the code". To be clear (and from my experience), it's much easier to adopt or translate a system that relies on the data for it being loaded from something rather than representing the data in hard-coded values.
public class Region {
private final String name;
public Region(String name) { this.name = name; }
public String getDisplayName() { return this.name; }
public String toString() { return getDisplayName(); }
}
public class Country {
//keeps a map/list of "regions", whether states or provinces or some other type
public Region getRegion(Object key); //lookup a region based on some key, I'll use strings here
}
Country america = /* a country supplied from somewhere */;
String name = america.getRegion("NY").getDisplayName(); //"New York"
This type of approach, while sub-optimal for hard-coded references in your code (like getRegion("NY")), is much more forgiving when you need to modify the data later or make a reference which is loaded from elsewheres (e.g. a user-supplied Region or lookup name). If you use a database, you can keep your code up-to-date without ever having to change the project, just the database itself. And in the end, since all of this data-related information is stored elsewheres, the overall amount of code to handle is vastly reduced.
You can also later add in something to support determining whether a given administrative region is a state or province or something else (which I think an enum is great for):
public enum RegionType {
STATE, PROVINCE, ADMIN_REGION, OTHER,
;
}
You can implement an EnumUtils class and add a static instance of it to every enum class. I implemented an example here: https://stackoverflow.com/a/48199147/7949301.
In my app, I have a Spinner being filled from an enum:
ArrayAdapter<myEnum> enumAdapter = new ArrayAdapter<Stroke> (parentActivity.getApplicationContext(), R.layout.simple_spinner_item, myEnum.values());
enumAdapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item);
enumSpinner.setAdapter(strokeAdapter);
This uses an override of the enum's toString() method to get a friendly name for the enum values to display in the Spinner. Currently my enum has strings hardcoded for the friendly names but I'd like to move these to strings.xml to support localization.
However, toString doesn't have access to a Context so I'm not sure how to resolve the resource ids.
Is there any way of getting localised strings in the toString() method of an enum?
If I understand this correctly, the real question here is how to get a Context from your enum, so that you can call Context.getString() to get localized versions of the Strings you need.
One approach, would be to set a static member variable of type Context in your application's onCreate() method, which is described in this answer. The idea here is that every time your application gets created or recreated, you'll hold on to the application context in a variable that's easy to get to.
Then, pass in the resource ID in the constructor of your enum values, and use the Context in your toString() method.
For example:
public enum Example {
HELLO(R.string.hello),
WORLD(R.string.world);
private int mResourceId;
private Example(int id) {
mResourceId = id;
}
#Override
public String toString() {
return App.getContext().getString(mResourceId);
}
}
#Override
public String toString()
{
return App.getContext().getString(id);
}
It's important that your enum class doesn't have any ties to your activity because if you want to use it in another application then you won't be able to if you are referencing a static context.
So a better way would be to return the resource id of the string back to the activity and then let the activity grab the string using the id from there.
So from your enum class you would have a method looking something similar to this:
public int getResourceId()
{
return resourceId;
}
Then in your activity I would build up a list containing an arraylist:
final List<String> enumList = new ArrayList<String>();
for ( final MyEnum status : MyEnum.values() )
{
enumList.add( getString( status.getResourceId() ) );
}
Then you can use enumList with your ArrayAdapter. Bingo :)
So now you have no ties to the enum class, and so if you are building another app that needs to use the same enum class you can easily do so.
Use static Application is always a bad practice, because not only it breaks Instant Run, but also this is against the decoupling principle thus makes modularization difficult to implement. Not to mention Android actually supports multiple Applications in a single process.
For this reason, I'd suggest define an inner class for the enum as your adapter entries.
enum Example {
A(R.string.label_a),
B(R.string.label_b);
Example(#StringRes int label) { mLabel = label; }
private #StringRes int mLabel;
class Entry {
private final Context mContext;
Entry(final Context context) { mContext = context; }
#Override public String toString() { return mContext.getString(mLabel); }
}
}
Then, build an array of Example.Entry for the adapter.
Arrays.stream(Example.values()).map(item -> item.new Entry(context)).toArray(Example.Entry[]::new)
I'm not 100% sure I understand what you are asking.
Is there any way of getting localised strings in the toString() method of an enum?
You can certainly #Override the toString() method inside of your myEnum to change how it is displayed:
public enum myEnum {
ONE("1"),
TWO("2");
private String pretty;
private myEnum(String pretty) {
this.pretty = pretty;
}
#Override
public String toString() {
// you can localise this string somehow here
return pretty;
}
}
I have an enum FooBar at class Clazz with falues FOO and BAR like this:
class Clazz {
enum FooBar{
FOO,
BAR
}
}
I now would like to use wicket getString() method to localize the values FOO and BAR. The best I can do is to define at i18n file
Clazz.FooBar.FOO=foo
Clazz.FooBar.BAR=bar
and I get values with this code
fooBar = FooBar.FOO;
getString("Clazz.FooBar." + fooBar.name());
I have heard that this could be achieved without Clazz.FooBar addition to the i18n query string, but the method to be called would be different. How to do this?
You can put this method in your base page/panel:
public String getString(Enum<?> value) {
Class<?> enclosingClass = value.getClass().getEnclosingClass();
String key = (enclosingClass == null ? "" : enclosingClass.getSimpleName() + ".")
+ value.getClass().getSimpleName() + "." + value.name();
return getString(key);
}
Then you can simply call it with
getString(Clazz.FooBar.FOO);
and it will return what you defined in the property file.
I will not advice you to directly store enum constant names in properties file the reason is simple two different enums can hold same name.
Below is the code I have come up with
class Clazz {
enum FooBar {
//StrId are keys from property file e.g. below
FOO("com.abc.classz.foobar.FOO"), BAR("com.abc.classz.foobar.BAR");
private final String strId;
private FooBar(String id) {
this.strId = id;
}
// toString can also be used here I am just keen on having seperate
// method
public String getName() {
//Load Value for strId from properties file
return null;
}
}
}
This will keep your enum and your i18n purpose separate and clear.
See below sample Enum class. You may want to customize it more depending on your needs.
public enum FooBar {
foo("foobar.foo"),
bar("foobar.bar");
private String key;
ErrorCodeEnum(final String key) {
this.key = key;
}
public String toString() {
return key;
}
}
then you can make the toString method to return key directly so you can use
getString(ErrorCodeEnum.ERROR1);
or you can override the toString method directly like below
public enum FooBar {
foo, bar;
public String toString(){
return getClass().getName()+"."+name();
}
}
You could simply define
FOO=foo
BAR=bar
in your properties and access it by
getString(fooBar.name());
or am I missing some point?
I was looking for something called EnumChoiceRenderer. The main idea is to give a EnumChoiceRenderer for e.g. DropDownChoise element and you're able to give parameters of the kind I was proposing in my question. Ok, in this solution you're able to give only
FooBar.BAR=bar
FooBar.FOO=foo
in your resource file but this is the closest I could find when I investigated this more with my spare time.
PS. Click the EnumChoiseRenderer in the beginning of this answer to see the article of this solution.
I have a number of enums that each have the same fields and the same methods.
public enum AddressSubType {
DOM("dom"), INTL("intl"), POSTAL("postal");
private final String keyword;
private AddressSubType(String keyword) {
this.keyword = keyword;
}
public String getKeyword() {
return keyword;
}
#Override
public String toString() {
return keyword;
}
}
public enum EmailSubType {
INTERNET("internet"), X400("x.400");
private final String keyword;
private EmailSubType(String keyword) {
this.keyword = keyword;
}
public String getKeyword() {
return keyword;
}
#Override
public String toString() {
return keyword;
}
}
Is there a way for these enums to share the fields and methods (like a parent class)? I know that it's not possible to extend enums. Thanks.
You could create a Value class
public class Value {
private final String keyword;
private Value(String keyword) {
this.keyword = keyword;
}
public String getKeyword() {
return keyword;
}
#Override
public String toString() {
return keyword;
}
}
Then you can create Classes with public static final values like this :
public class AddressSubType extend Value {
public static final AddressSubType DOM = new AddressSubType("DOM");
public static final AddressSubType INTL = new AddressSubType("intl");
...
private AddressSubType(String keyword) {
super(keyword);
}
}
I would probably combine them into a single enum object where some are initialized with an "Postal" flag set to true and some have the "email" flag set to true since the two are really just different "types" of addresses.
You can then have it return iterators for either if you wish to access them separately or you can iterate over the whole thing.
You may also find some of the rest of your code becoming simplified, for instance just having a collection of "Address"es and checking at runtime to see if a given address is email or postal.
But it depends on how similar they really are.
You can declare an interface that they both can implement. This would allow you to pass either enum type as an argument to a method that only cares about specific methods on that inerface. However, this will only allow you to "share" the method signatures, not the fields or the method implementations.
If your enums are as trivial as in the given example, you don't have any significant amount of code repetition, so this probably isn't a problem. If you find that your methods have more complex, repetitive code, you should consider delegating that responsibility to a separate class.
If you really want to model an inheritance pattern (e.g. EmailAddress "is a" Address), then you'll need to get away from enums. You could just use some static fields to simulate the enum pattern, but have each of them be an instance of a specific class.
I will be the one to say it. This is an awful idea.
You should use enum types any time you need to represent a fixed set of constants. That includes natural enum types such as the planets in our solar system and data sets where you know all possible values at compile timeāfor example, the choices on a menu, command line flags, and so on. source
The enum does not care about anything else except the hard coded values inside. Typically when one decides to group things in an Object Oriented way, they make sure that all of the objects are related. By virtue of being an enum these files are no more related than two classes that are subtypes of Object. If you are looking to have shared functionality between all enums in your domain you will want to look at some static functions, or a utility class as it is often referred to (this has its own series of issues at the end of the day). Essentially the class will have a series of functions that encapsulate all the shared logic, the signature will generally look like so:
function foo(Enum enumeration)
There's not much you can do in this case, and even in a more complex example, the best place to put common code might be in a utility class that all the enums could use, or in a separate class that would be included in the enums via composition (each enum would have an instance of that class, perhaps called Keyword).
If the code for the toString method were complex and you didn't want to restate it in each enum, or move it to a contained object, Java 8 has a mechanism that you could use. It is overkill in this example. You could define an interface that your enums would all use. The state (keyword) must still live in your enums, since interfaces cannot have state, but starting with Java 8 you can provide default implementations of methods:
public interface Common {
String getKeyword();
String toString() default {
return getKeyword();
}
String toDescriptiveString() default {
char firstLetter = getKeyword().charAt(0);
boolean vowel =
firstLetter == 'a' || firstLetter == 'e' ||
firstLetter == 'i' || firstLetter == 'o' ||
firstLetter == 'u' || firstLetter == 'x';
// the letter x is pronounced with an initial vowel sound (eks)
return (vowel?"an ":"a ") + getKeyword() + " address";
}
}
Your enums would implement this interface:
public enum AddressSubType implements Common {
DOM("dom"), INTL("intl"), POSTAL("postal");
private final String keyword;
private AddressSubType(String keyword) {
this.keyword = keyword;
}
#Override
public String getKeyword() {
return keyword;
}
#Override
public String toString() {
return Common.super.toString();
}
}
public enum EmailSubType implements Common {
INTERNET("internet"), X400("x.400");
private final String keyword;
private EmailSubType(String keyword) {
this.keyword = keyword;
}
#Override
public String getKeyword() {
return keyword;
}
#Override
public String toString() {
return Common.super.toString();
}
}
Notice the strange new syntax in the toString methods. The rule for default methods in interfaces is that method resolution always prefers class methods over interfaces. So even though we provide a default implementation of toString in Common, the one in the enum will take precedence, and the one in Object would if there wasn't one in the enum. So if you want to use a default method from an interface that supersedes one of the methods from Object (like toString, or hashCode, or equals), then you have to call it explicitly with this new interface.super.method() syntax.
We don't have to jump through any extra hoops for the toDescriptiveString method, though. That one is brand new in interface Common, and it isn't provided by our enums, so they get the default implementation provided by the interface. (If they wanted to override it with their own method, they could, just like any other inherited method.)
We can use the default methods like any other methods of an object, of course:
public class Test {
public static void main(String[] args) {
for (AddressSubType a : AddressSubType.values()) {
System.out.println(a.toDescriptiveString());
}
for (EmailSubType e : EmailSubType.values()) {
System.out.println(e.toDescriptiveString());
}
}
}
Which prints out:
a dom address
an intl address
a postal address
an internet address
an x.400 address
In this case, however, if it wasn't for the rather verbose toDescriptiveString method, the enum classes wouldn't be a bit shorter with interface Common than they would be without. Default methods in interfaces will really shine when it comes to adding new functionality to existing interfaces, something not possible without breaking all implementers of an interface in previous versions of Java.
All of this is based on the as-yet-incomplete Java SE 8 with Lambda. You can download a pre-release build, but be aware that it is a work in progress.
I have data type which contains 100 properties and 100 getter methods (getproperty1....getproperty100).
I get an input from the user like
Property1
Property2
.
.
Property100
How can I invoke in a quick way the method in this logic
For property1 I need to invoke getproperty1
For propertyI I need to invoke getpropertyI
How can I do this with out using if else, or switch statement or reflection in an efficient way.
Thanks
Your best bet is probably going to be an array or hashmap of some type, and access it by index/key:
public class DataType {
private Map<String, DataProperty> data = new HashMap<String, DataProperty>();
public DataProperty getProperty(String key) {
return data.get(key);
}
public void setProperty(String key, DataProperty value) {
data.put(key, value);
}
}
Although, 100 properties seems like a lot... see if you should break it up or otherwise re-organize it.
You could refactor the class to be a Map. If you have a large number of objects like that it seems more along the lines of a map than an object.
Map<String, Object>
1. If you need to invoke multiple methods I would suggest using the Strategy design pattern. In it's simplest form you could try
public interface Command<T> {
public T getProperty();
}
and then create as many implementations as necessary.
2. If you are only interested in the return type and not the actual invokation the Map<String, T> would be a better alternative.
3. If you want to pass around the information in your program a good alternative would be to use the enum approach
public enum Command {
Property1("some value"),
Property2("some other value");
private String str;
public Command(String str) {
this.str = str;
}
public String getVal() {
return str;
}
}
Which can be used like
Command cmd = ...
String value = cmd.getVal();