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.
Related
Let's assume we have an enum, that represents searchable fields:
enum SearchableFields {
ALL,
FIELD1,
FIELD2,
FIELD3;
}
This enum is displayed via a (combobox) selection inside a GUI. At runtime, I want to evaluate the selection of this combobox and search accordingly.
Depending on the selection, I need to retrieve the fields to search from a POJO (example below) via a getter.
class FieldPojo {
private String field1;
private String field2;
private String field3;
...
public String getField1() {
return field1;
}
...
}
I currently use a switch statement to evaluate the selection of SelectableFields and to then retrieve the correct field(s) to search:
private String retrieveField(FieldPojo f) {
switch (selectedField) {
case ALL:
return retrieveAll(); // method that retrieves all available fields
case FIELD1:
return f.getField1();
...
}
This does work, however I feel like it's clunky.
Question:
Is there a more concise way to do this without evaluating the enum via a switch? (Java 8)
You could store a reference to the getter in your enum constants:
enum SearchableFields {
ALL(FieldPojo::retrieveAll),
FIELD1(FieldPojo::getField1)
private final Function<FieldPojo, String> accessor;
SearchableFields(Function<FieldPojo, String> acccessor) {
this.accessor = accessor;
}
public String get(FieldPojo fp) {
return accessor.apply(fp);
}
}
You can create a static map instead of the switch-case.
private static final Map<SearchableFields,Supplier<String>> searchableFieldsToFieldPojo = Map.of(
ALL, this::retrieveAll,
FIELD1, FieldPojo::retrieveAll
);
And then you can access via:
searchableFieldsToFieldPojo.get(selectedField).get();
Given that you can modify all parts of the code, you have several options:
Put the retrieveField into the class FieldPojo and modify it's parameter so it takes the enum SearchableFields as parameter.
Put the fields of FieldPojo as values into a map with a key of type SearchableFields. You can then decide whether you want to have "ALL" as an extra entry of the map or handle it as special case in a method similar to retrieveField. You could use this to have a "default" handling if you want to update the enum but not the FieldPojo class.
You put retrieveField into the class FieldPojo together with the SearchableFields enum - since only FieldPojo knows, which fields it actually provides as searchable fields.
You use introspection to gather the list of possible searchable fields and also access their contents.
Depending on your real requirements (you only showed a very abstract and specific version of them) one or the other method might be "the right one" for you. I would actually prefer the "everything into FieldPojo" as the most robust one, but on the other hand if you are not able to change FieldPojo and have to handle many different classes like it, the introspection variant might be the right one. (Be aware that it is fragile in terms of security and also probably very slow.)
Enums can contain method definitions, so one way is to define the method that retrieves the field name based on the enum value. I assume you have the actual field name stored as a member field also. Then you can override the method for the special ALL value:
enum SearchableFields {
ALL("all") { // all is just a placeholder in this case
#Override
String retrieveField(FieldPojo f) {
// logic for all fields
}
},
FIELD1("field1"),
FIELD2("field2"),
FIELD3("field3");
SearchableFields(String fieldName) {
this.fieldName = Optional.of(fieldName);
}
SearchableFields() {
fieldName = Optional.empty();
}
private final Optional<String> fieldName;
String retrieveField(FieldPojo f) {
if (fieldName.isPresent()) {
return (String) f.getClass().getField(fieldName.get()).get(f);
} else {
// ...
}
}
}
is there a way in Java to create a custom class based on a primitive Java base class to give the derived class a semantic?
Example: I want to create a LinkedHashMap<LastName, Age> ages where LastName is a custom type of String and Age a custom type derived from Integer. I just tried to create my own class
public class LastName extends String {
//(no code in here, LastName has the same functionality as String)
}
but this is not possible. Is there another way to achieve what I want?
What you probably want is type-aliases. Java does not support this.
The best way would be to create a wrapper class, something like this:
class LastName{
private String value;
}
Another way is to name your variables correctly, eg don't do:
String string = "Smith";
But rather do:
String lastName = "Smith";
or just document your code with comments and javadoc.
Sidenote: If you still want to use type aliases you may want to use the Kotlin programming language which can compile to java-code (and more).
NO
Those classes are final.
Usual way to deal with it is to create wrapper class.
For example:
public class LastName {
private final String value;
public LastName(String value) {
this.value = value;
}
public String get() { return value; }
}
A String implements a CharSequence interface, just like StringBuffer and StringBuilder class. You can follow the same process.
public final class LastName implements CharSequence {
...
}
I want to ask about nested enums. I am working with old code and i found very strange construction that i not really good understand.
I have this enum :
public enum DbEngines {
ORACLE("oracle", "set define on", "set define off")
, POSTGRESQL("postgresql", "--TODO set define on", "--TODO set define off");
private final String dbEngine;
private String setOn;
private String setOff;
DbEngines(String dbEngine, String setOn, String setOff) {
this.dbEngine = dbEngine;
this.setOn = setOn;
this.setOff = setOff;
}
public String getSetOn() {
return setOn;
}
public String getSetOff() {
return setOff;
}
public String toString() {
return this.dbEngine;
}
}
I added private String to this enum, that are engine specific, so it is good place for me here. The problem is, that in some places in method declaration i see something like that
public someMethod(Enum<DbEngines> engine, ...)
And it worked perfectly without methods, but now, after changing, I couldn't call public getters of this enum. But if i change to :
public someMethod(DbEngines engine, ...)
it works without any problems with all public getters. Maybe someone could explain that?
Enum in Java is the base class for all enumeration types. One can think of it as similar to Object class.
Just like one can hold reference of object of any class using the reference of type Object, one can refer to an enumeration type using the reference of type Enum.
Object o = new Integer(10);
Enum e = DBEngine.ORACLE;
One cannot invoke a method present in inherited class but absent in superclass using the reference of superclass.
Similar explanation over here.
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.
I want to define an enum type with two constants whose "value" is the same. I call these two constants as duplicates. Consider the following example: I want to define a list of browser types, and I want to have both a literal "IE" and "InternetExplorer", as below:
enum Browser {
CHROME("chrome"),
FIREFOX("firefox"),
IE("ie"),
INTERNETEXPLORER("ie");
String type;
Browser(String type) {
this.type = type;
}
}
However, with this, the following code will fail,
Browser a = Browser.IE;
Browser b = Browser.INTERNETEXPLORER;
Assert.assertTrue(a==b);
The only workaround I can think of is that to add a value() method of the Browser type that returns the internal value of the browser instance. And the equality test code would be
Assert.assertTrue(a.value()==b.value())
This is not nice. So does anyone have a better idea?
Why does Java not allow to override methods like equals() of Enum<T> class?
EDIT:
OK, thanks for the answers and comments. I agree that my original thought was against the purpose of enum. I think the following changes can meet my need.
public enum Browser {
CHROME,
FIREFOX,
IE;
public static Browser valueOfType(String type) {
if (b == null) {
throw new IllegalArgumentException("No browser of type " + type);
switch (type.toLowerCase()) {
case "chrome":
return Browser.CHROME;
case "firefox":
return Browser.FIREFOX;
case "ie":
case "internetexplorer":
case "msie":
return Browser.IE;
default:
throw new IllegalArgumentException("No browser of type " + type);
}
}
}
Hierarchical enumeration trick is probably what you want in this case. Although it doesn't solve the comparison problem, it provides a very nice alternative to you problem.
http://java.dzone.com/articles/enum-tricks-hierarchical-data
I quote the codes from the site above directly with slight simplification:
public enum OsType {
OS(null),
Windows(OS),
WindowsNT(Windows),
WindowsNTWorkstation(WindowsNT),
WindowsNTServer(WindowsNT),
WindowsXp(Windows),
WindowsVista(Windows),
Windows7(Windows),
Unix(OS),
Linux(Unix),
;
private OsType parent = null;
private OsType(OsType parent) {
this.parent = parent;
}
}
One simple way to do this is to put instance variables inside your enum class.
enum Browser {
CHROME,
FIREFOX,
INTERNETEXPLORER;
public static final Browser IE=INTERNETEXPLORER;
}
Then, IE should just act as an alias to INTERNETEXPLORER, and you can use them interchangeably.
EDIT: Thanks to big_m for suggesting to make IE final!
EDIT2: This trick should work in most code, but there's an exception if you are using switch/case. Here's an example:
Browser b;
switch(b){
case Browser.CHROME:
//code for chrome
break;
case Browser.IE: // <---- SYNTAX ERROR, use Browser.INTERNETEXPLORER in this case
//code for internet explorer
break;
}
Each enum mutually extends class Enum that defines equals() as final. This is done because enum is not a regular class. JVM guarantees that each enum element is unique, i.e. exists only one instance of each element within one JVM.
This is required for example for using enums in switch statement etc.
What you are trying to do is to go against this concept: you want to have 2 equal members of the same enum.
However I can offer you other solution: define only one IE member. Define String[] member into the enum and method that can find appropriate member by any alias:
public enum Browser {
CHROME("Chrome"),
FIREFOX("FireFox"),
IE("IE", "MSIE", "Microsoft Internet Exporer"),
;
private String[] aliases;
private static Map<String, Browser> browsers = new HashMap<>();
static {
for (Browser b : Browser.values()) {
for (String alias : b.aliases) {
browsers.put(alias, b);
}
}
}
private Browser(String ... aliases) {
this.aliases = aliases;
}
public static Browser valueOfByAlias(String alias) {
Browser b = browsers.get(alias);
if (b == null) {
throw new IllegalArgumentException(
"No enum alias " + Browser.class.getCanonicalName() + "." + alias);
}
return b;
}
}
You can't override the equals() method for an enum, but even if you could the == operator does not execute the equals() method: There is no way to make a == b be true for your example.
The closest I can think of is a utility (static) method:
enum Browser {
CHROME("chrome"),
FIREFOX("firefox"),
IE("ie"),
INTERNETEXPLORER("ie");
private final String type;
Browser(String type) {
this.type = type;
}
public String getType() {
return type;
}
public static boolean equals(Browser b1, Browser b2) {
return b1.type.equals(b2.type);
}
}
Also note that I would make type private final. As it is, you can do this:
IE.type = "netscape"; // would be allowed
What you should really do is normalize the conversion of input to enum, i.e. if your input (from user/data store) is IE/INTERNETEXPLORER, it should be resolved to Browser.IE. That would remove a lot of redundant checks in the code to see if the enum is IE or INTERNETEXPLORER.
As Written In EnumClass
but extending this class does not make a class an enumeration type, since the compiler needs to generate special information for it.
In order to support equality based on reference equals method is final.
You can use EnumSet to create EnumSet of Related enums and then you can use contains method.
public static EnumSet<Browser> MSE = EnumSet.of(Browser.IE,
Browser.INTERNETEXPLORER);
So your code will look something like below.
public enum Browser {
CHROME("chrome"), FIREFOX("firefox"), IE("ie"), INTERNETEXPLORER("ie");
String type;
Browser(String type) {
this.type = type;
}
public static EnumSet<Browser> MSE = EnumSet.of(Browser.IE,
Browser.INTERNETEXPLORER);
public static void main(String[] args) {
System.out.println(MSE.contains(Browser.IE));//true
System.out.println(MSE.contains(Browser.INTERNETEXPLORER));//true
}
}
I would remove the string value and the duplicate IE instance, its of no use ...
enum Browser {
CHROME,
FIREFOX,
IE
If you must have lower case representation just convert from enum name when you need it.