Dynamically Generate enum values - java

I have been looking around for a good example on how I might go about dynamically generating enum values. I found a couple good articles, however I am looking for a compile time solution and what I've found is only at run time.
Does anyone know if this is even possible? I have yet to find anything that hints it might be.
Thanks!
EDIT:
For clarification: I would like to be able to read values out of a database and populate an enum with those values.
In a perfect world, I would like my enum class to look like the following:
public static enum STATE {
/* populated from DB if possible */
MA("high taxes", 6),
NH("low taxes", 3),
...
...
private String desc;
private in rating;
public STATE (String description, int rating) {
this.desc = description;
this.rating = rating;
}
}

Well, here is an approach that does it on class initialization. Which is runtime:
public enum States {
MA, NH; // ...
private String description = "Description of " + name() + " not found in database.";
private int rating;
// Static initialization is performed after the enum constants
// are initialized, but can still change *non-final* fields
// in the constants
static {
String sql = "SELECT abbreviation, description, rating "
+"FROM states "
+"WHERE abbreviation IS NOT NULL ";
ResultSet rs;
// Open connection, create statement, execute, retrieve
// result set. IMPORTANT: catch and properly handle all
// checked exceptions, or else you'll get a nasty
// initialization error. OTOH, you may not want your
// application to start if this fails.
while ( rs.next() ) {
String abbreviation = rs.getString(1);
String description = rs.getString(2);
int rating = rs.getInt(3);
States st;
try {
// Get the enum constant that matches the abbreviation.
st = valueOf(abbreviation);
// Set the values in that constant
st.description = description;
st.rating = rating;
} catch ( IllegalArgumentException e ) {
// This exception happens when the abbreviation
// doesn't match any constant. If you don't put
// anything here, such values will be silently
// ignored. If you don't catch, such values will
// throw an initialization Error.
}
}
// Clean up all database-related stuff.
}
// Only getters, no setters, as values are all
// set from database in the static initialization.
public String getDescription() {
return description;
}
public int getRating() {
return rating;
}
}
With this definition, you can use the enum constants in your program, and the values in the description and rating field will be loaded at class initialization from database. Note that I gave a default value to description which will show up if the particular state's abbreviation is not in the database.
But as I said, this is run time. Although not completely impossible, I can see no sense in loading the values from database at compile time, as these values will stay fixed when you use your resulting .class file or jar. When you change values in your database, the values seen by the application will still be the one hard-compiled into the enum. In fact, you won't even need the database to be up to run the application.
But if you insist on doing this for some reason, well, no IDE will support this directly, I suppose. But you could probably write a script that manipulates the text of your enum java file, and use that script in a pre-compile phase in your build tool (maven, ant...). You'll probably need to write your class much like the above, only with the static initializing block empty. You'll need a clean copy outside of your src directory, and run the script so that it fills up the static initialization block with text derived from the database, and writes the result inside your src directory.
In short: not recommended, system/tool dependent, not useful, but also not impossible.

You can use a class for this at the exact same place where you would have put the enum and get similar behaviour:
public final class STATE {
public static final STATE MA;
static {
// SELECT desc, rating FROM myTable where name = 'MA' ... or what suits you
...
MA = new STATE(myDesc, myRating);
}
...
private String desc;
private int rating;
private STATE (String description, int rating) {
this.desc = description;
this.rating = rating;
}
public String getDesc() {
return desc;
}
...
}
Because of the private constructor and because the class is final and only has getters you can only assign the predefined values to STATE. This means you can compare a STATE variable v just like that v == STATE.MA because they all use the same reference.

If you have fixed names, but the "values" are loaded from the database, you can use an enum constructor:
public enum Data {
A("a"), B("b"), C("c");
SomeType someName;
public Data(String s) {
someName = MyDatabase.loadValue(s);
}
public SomeType getSomething() {
return someName;
}
}
The constructor is called at class initialization.

Related

How to avoid calling this.field for every field in a java class

Is there a way to avoid calling this.field for every field in a class ?
public class Test {
private String name;
private String email;
public Test(String name, String email) {
// I want to avoid this
this.name = name;
this.email = email;
}
public Test(Test test) {
// Something like this would be perfect, setting both name and email to test
this(test);
}
}
The use of this is only required in cases of name collisions, to resolve the ambiguity.
Some programmers like me prefer using the this. prefix routinely, whereas other use only where necessary.
See Answer by Wasserman for an example of how to avoid naming collision.
Use the IDE, Luke
Your IDE will generate constructors, accessors (getters/setters), equals & hashCode, toString, and so on. So you need not type this.; let the machine do the typing.
Use custom settings to control whether you want the IDE to include or omit this. prefixes.
record
You may be interested in using the records feature, new in Java 16+. A record is a brief way to write a class whose main purpose is to communicate data transparently and immutably.
With a record, by default, the compiler implicitly writes the constructor, getters, equals & hashCode, and toString. The implicitly created constructor populates each and every member field on your behalf. You write none of that code.
Here is your entire example class when written as a record. No this required. All your member fields are automatically assigned.
public record Test ( String name , String email ) {}
Be cautious in using records. The reason for their invention was not writing less code. The reason was to provide an explicit mechanism for transmitting immutable data transparently, a “nominal tuple” in academic-speak. Less boilerplate coding is merely a nice side-effect. I highly recommend reading JEP 395 for more explanation.
Tip: You can combine the two points of this Answer. Ask your IDE to generate a full-blown class by starting with a record.
Write a record with all your member fields listed in the parentheses.
Invoke your IDE to convert from a record to a class.
Voilà, you have a complete class with constructor, accessors, equals & hashCode, and toString all written out with an absolute minimum of typing by you.
For example, in IntelliJ 2022, choosing Convert record to class from the light-bulb icon menu turns this:
public record Test ( String name , String email ) {}
… into this:
package work.basil.example.recs;
import java.util.Objects;
public final class Test
{
private final String name;
private final String email;
public Test ( String name , String email )
{
this.name = name;
this.email = email;
}
public String name ( ) { return name; }
public String email ( ) { return email; }
#Override
public boolean equals ( Object obj )
{
if ( obj == this ) { return true; }
if ( obj == null || obj.getClass() != this.getClass() ) { return false; }
var that = ( Test ) obj;
return Objects.equals( this.name , that.name ) &&
Objects.equals( this.email , that.email );
}
#Override
public int hashCode ( )
{
return Objects.hash( name , email );
}
#Override
public String toString ( )
{
return "Test[" +
"name=" + name + ", " +
"email=" + email + ']';
}
}
Caveat: That result may not be the default. I may have altered the settings in IntelliJ.
Sorry, the only way to avoid this is to have different names for your constructor parameters and for your class fields.
public Test(String _name, String _email) {
// I want to avoid this
name = _name;
email = _email;
}
That said, you might have better luck using Java 16+'s record syntax.
As suggested, using records is the easiest way:
public record Test (String name, String email) {
}
That's all you need. What you then get:
A constructor that takes all arguments, in the same order as the field list
A method for each field. This does not start with get. In this case, the methods are name() and email().
equals, hashCode and toString implementations that use all fields.
There is no need for a copy constructor, because every field is automatically final.
If you want, you can add extra constructors. However, they must delegate to the automatically generated constructor, because that's the one that sets the fields. Adding additional utility methods is also fine.
And if needed, you can add validation to the generated constructor. There's special syntax that allows you to omit all the field names:
public record Test (String name, String email) {
public Test {
Objects.requireNonNull(name);
Objects.requireNonNull(email);
}
}
The assignments are done for you, there's no need to type those either.
You need this.x everytime, if there are 2 or more variables, which are called x and you want to call the attribute variable x.
The this keyword is used, to point on an attribute variable of the created instance (object) of the class.
There could be an attribute, that is called x, and a local variable which is called x too.

What's the best way to change attributes of objects stored in an ArrayList or HashMap?

I have to do a little exercise (homework, like a friendlist) in Java, and i'm a little stuck on one of the tasks that i have to implement in my program.
The exercise is about storing some friend-objects with a variety of attributes in a container-class and implementing some methods in the container-class for various tasks on the friend-objects.
The overall exercise is not a problem at all, but i'm quite unconvinced that my solution is the way to go. I hope you can give me some tips here.
The method that is left over, should be something like a "updateFriend" method, with which you can set the value of a given attribute to a new value, straight from the container-class.
I've already set up my friend-class with a handfull of attributes (e.g. prename, lastname, date of birth, adress, and so on) an getters/setters for all of them. I've also implemented the container-class (as an ArrayList), but i can't seem to find an elegant way to implement this specific method. My updateFriend()-method right now takes three parameters.
1.The specific id of the friend-object
2.The name of the attribute that i want to change
3.The new value of the attribute
It uses an enum to check if the entered attribute is an existing attribute and if yes, the method searches the ArrayList for the object that contains that attribute and should overwrite the existing value. It gets a little bulky, as i have implemented a switch on the enum, that calls the fitting setter-method for each attribute of the friend, if the type in attribute exists at all.
So basically the friend-class looks like this:
public class Friend {
private static int friendCount = 1;
private String firstname;
private String lastname;
private LocalDate dateOfBirth;
private String phonenumber;
private String mobilenumber;
private String eMail;
private Adress home;
private int friendID;
//Getters & Setters
...
}
The method that gives me problems in the container-class looks something like this at the moment:
public void updateFriend(int id, String toChange, String newValue)
{
for(Attribute a : attribute.values())
{
if(String.valueOf(a).equalsIgnoreCase(toChange))
{
for(Friend f : friends)
{
int counter = 1;
if(f.getID() == id)
{
switch(a)
{
case FIRSTNAME:
{
f.setPreName(neuerWert);
break;
}
//a case for each attribute
}
I'm quite certain that my take on the given method is messy, slow, and cumbersome. What would be an elegant way of solving this?
Excuse my wording and thanks in advance, greets.
I would suggest 3 performance improvements.
Use HashMap instead of List with key as id. Since, id will be unique, it will take O(1) time to get the relevant object for modification instead of spending O(n) time on List iteration.
You can change the type of toChange parameter from String to enum. This will avoid enum to String conversion and then comparing it.
Since, you are already doing validation of the attribute to be modified and you must be following standard java convention while naming your getters and setters, you can use reflection to call the method on the Friend object by creating the method name from attribute name like set{Attributename}.
Okay, lets start using the enum Attribute to handle all the changes (Since you already holding the attribute values)
Attribute Enum
public enum Attribute {
FIRSTNAME("fname", (friend, name) -> friend.setFirstname(String.valueOf(name))),
LASTNAME("lname", (friend, lname) -> friend.setLastname(String.valueOf(lname))),
DATEOFBIRTH("dob", (friend, dob) -> friend.setDateOfBirth((LocalDate) dob)),
PHONENUMBER("pno", (friend, pno) -> friend.setFirstname(String.valueOf(pno))),
MOBILENUMBER("mno", (friend, mno) -> friend.setFirstname(String.valueOf(mno)));
private String attributeName;
private BiConsumer<Friend, Object> attributeSetter;
public static Attribute getAttributeSetterByName(String attributeName) {
return Arrays.stream(Attribute.values())
.filter(attribute -> attribute.getAttributeName().equalsIgnoreCase(attributeName))
.findFirst()
.orElseThrow(() -> new RuntimeException(String.format("Invalid Attribute name - %s", attributeName)));
//.orElse(null);
}
//Getter, Setter & Args Constructor (Use Lombok to reduce Boiler Plate code)
}
Update Logic
public void updateFriend(int id, String toChange, String newValue) {
Attribute attribute = Attribute.getAttributeSetterByName(toChange);
for (Friend friend : friends) {
if (friend.getId() == id) {
attribute.getAttributeSetter().accept(friend, newValue);
break;
}
}
}
You can use a java.util.function.Consumer<T> object to change an object inside your container where you have all the type safety you get. Instead of having magic strings and string arguments for values, which might not be even for string fields, you can work directly on the objects type:
public void updateFriend(int id, Consumer<Friend> c) {
// find the friend object
Friend found = null;
for (Friend f: this.friends) {
if (f.getId() == id) {
found = f;
break;
}
}
if (found == null) {
throw new IllegalArgumentException("There is no friend object with the given id");
}
// use the friend object.
c.accept(found);
}
You can use this method like this:
container.updateFriend(42, f -> f.setVorName("abc"));
container.updateFriend(9, f -> f.setAddress(some_address_object));

Best way to get an enum by numeric id

I need to create an enum with about 300 values and have the ability to get its value by id (int). I currently have this:
public enum Country {
DE(1), US(2), UK(3);
private int id;
private static Map<Integer, Country> idToCountry = new HashMap<>();
static {
for (Country c : Country.values()) {
idToCountry.put(c.id, c);
}
}
Country(int id) {
this.id = id;
}
public static Country getById(int id) {
return idToCountry.get(id);
}
}
That enum is going to be used a lot, so I'm wondering if this is the best possible solution performance-wise.
I've read http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html over and over again, but couldn't find the part that describes at what time the
static {
}
block is called, and if it is guaranted that this will be called only once. So - is it?
In case if the first country id is 0 and ids are incremented by 1, you may use the next approach:
Cache enum values in array. Enum.values() returns elements in the same order as they declared in enum. But it should be cached, as it creates new array every time it is invoked.
Get values from cached array by id, which will be array index.
Please, see the code below:
enum Country {
A, B, C, D, E;
private static final Country[] values = Country.values();
public static Country getById(int id) {
return values[id];
}
}
UPDATE: To get Country's id, ordinal() method should be used. And to make getting id code clearer, the next method can be added to the enum:
public int getId() {
return ordinal();
}
Static initializer blocks are called once when the class is initialized. It's not guaranteed to be called once, but it will be unless you're doing something exotic with class loaders.
So, your approach is probably fine from a performance perspective. The only changes I'd propose would be to make your fields final.
An alternative way to represent the mapping could be to store elements in an array (or a list):
Country[] countries = new Countries[maxId + 1];
for (Country country : Country.values()) {
countries[country.id] = country;
}
You could then look them up by element index:
System.out.println(countries[1]); // DE.
This avoids the performance penalty of having to box the id in order to call idToCountry.get(Integer).
This of course requires you to have non-negative IDs (and ideally the IDs would be reasonably contiguous, to avoid having to store large runs of null between countries).
First you don't need to have a static block to create the map. You can just add your code to constructor where each component adds itself to your map. Enum is ALWAYS a sigleton so your constructor is guaranteed to be called only once (per a enum value) Also you don't need to even have ID as Enum has method public final int ordinal() that returns its zero-based sequential number in the enum. In your case ordinals would be 0 for DE, 1 forUS and 2 UK.
Here is an example:
public enum Country {
DE, US, UK;
private static Map<Integer, Country> idToCountry = new HashMap<>();
Country() {
idToCountry.put(this.ordinal(), this);
}
public static Country getById(int id) {
return idToCountry.get(id);
}
}
You can try this one too. Simple as it shows.
enum Country {
DE(1), US(2), UK(3);
public int id;
Country(int id) {
this.id = id;
}
public static Country getCountry(int id) {
Country[] c = new Country[Country.values().length];
c = Country.values();
return c[id];
}
}
Thanks a lot.

take the value of enum and covert it to String

I should take from a variable enum its value and transform it to string.how can i do?
here it is the type enum:
public enum State{
b,c,p;
};
now i have to insert into an object String one value.
You might use enum.name orenum.toString to get the name of the enum constant, or enum.ordinal to get the ordinal position.
you can use name() or toString(), so :
State aState = State.c;
String strState = aState.name();
See here the official java reference for more information...
State.b.toString() will return "b". The same goes for the other ones.
Usually,
State state = ...;
String string = state.toString();
should work, but it is not recommended since someone might override toString for some other purpose.
Instead the method you are looking for is
String string = state.name();
As an aside, your enumerated stated should always be all in capitals, and they should have descriptive names. It's not a language rule, but a convention. For example enum State { ON, OFF, PAUSED; }.
I tend to do something more complicated, but I find that it's more flexible:
public enum MyEnumeration {
SOME_NAME("Some Name"),
OTHER_THING("Other Thing"),
...
MORE_VALUES("More Values"),
private final String displayName;
private MyEnumeration(String displayName) {
this.displayName = displayName;
}
public String getDisplayName() {
return displayName;
}
}
This way, I use standard capitalization for my enums in code, but can have a more presentable name for them.
This trick can also be used to replace ordinal, by initializing a number, and then you don't need to worry about rearranging your enums.
Method #1: Using the built-in toString() and name() methods
If you want to print a String that is the same as the value of the State, then you can use the toString() method, or the name() method.
System.out.println(State.b); // Prints "b"
System.out.println(State.c); // Prints "c"
System.out.println(State.p); // Prints "p"
Method #2: Using a constructor to create a custom mapping
If you want to have a custom String associated with each of those states, you can use a constructor to associate a particular value with each enum value:
public enum State{
b("State B"), c("State C"), p("State P");
private String longName;
private State(String longName) {
this.longName = longName;
}
#Override
public String toString() {
return this.longName;
}
};
Of course, if you don't want to break the default toString() usage, you can create a different method called getFullName(), for example, to return the custom value.

Can I give an enum an attribute in VB.NET (like I can do in Java)?

In Java I can do something like this:
enum Country {
IRELAND("Europe"),
FRANCE("Europe"),
NIGERIA("Africa"),
THAILAND("Asia");
private String continent;
Country(String continent) {
this.continent = continent;
}
public String getContinent() {
return continent;
}
}
which allows me to do something like:
Country country1 = getCountryFromSomewhere();
Country country2 = Country.FRANCE;
System.out.print("country1 is in " + country1.getContinent());
System.out.print("country2 is in " + country2.getContinent());
Is it possible to do the same thing in VB.NET i.e. add the continent attribute to the country enum?
(Apologies for using C# throughout - I believe the concepts are more about .NET than the language you happen to use; hopefully you're better at reading C# than I am at writing VB.)
Not directly - enums in .NET are just integer types with names for some of the values.
The closest you can come in .NET is to create a type with a fixed set of values. For example, in your case:
public sealed class Country
{
public static readonly Country Ireland = new Country("Europe");
public static readonly Country France = new Country("Europe");
public static readonly Country Nigeria = new Country("Africa");
public static readonly Country Thailand = new Country("Asia");
private readonly string continent;
public string Continent { get { return continent; } }
private Country(string continent)
{
this.continent = continent;
}
}
(I assume the VB.NET would be very similar.)
Note that this doesn't let you switch on the enum values.
If you want polymorphism, you can create nested subclasses which can still call the private constructor, which prevents any other subclasses being created.
One alternative to this is to use attributes on normal enums:
[AttributeUsageAttribute(AttributeTargets.Field)]
public class ContinentAttribute : Attribute
{
// etc
}
public enum Country
{
[Continent("Europe")] Ireland = 1,
[Continent("Europe")] France = 2,
...
}
You'd then need to use reflection to get at the ContinentAttribute and retrieve the string.
Note that here there isn't really a fixed set of values - you could write:
Country country = (Country) 15;
At that point you can't get the continent for it, and if you pass it to any methods which expect it to be a real country, you've got problems. That isn't the case with the earlier solution, where you really are restricted to those few values (and null).
Here is the code:
Imports System.ComponentModel
Imports System.Reflection
Public Enum enumOrderStatus
<Description("None")>
None
<Description("Sent")>
Sent
<Description("Accepted")>
Accepted
<Description("Cancelled")>
Cancelled
<Description("Declined")>
Declined
End Enum
Public Function GetEnumDescription(ByVal EnumConstant As [Enum]) As String
Dim fi As FieldInfo = EnumConstant.GetType().GetField(EnumConstant.ToString())
Dim aattr() As DescriptionAttribute = DirectCast(fi.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())
If aattr.Length > 0 Then
Return aattr(0).Description
Else
Return EnumConstant.ToString()
End If
End Function
I used this solution instead:
Declare enum:
Private Enum Country
IRELAND
FRANCE
THAILAND
End Enum
Declare and initialise Dictionary (aka a map):
Dim countryContinentMap As IDictionary(Of Country, String) = New Dictionary(Of Country, String)
countryContinentMap.add(Country.IRELAND, "Europe")
countryContinentMap.add(Country.FRANCE, "Europe")
countryContinentMap.add(Country.THAILAND, "Asia")
which allows me to get the continent like this:
Dim franceContinent As String = countryContinentMap(Country.FRANCE)
Here is how I solved this in my application. Still looking for something even easier.
What do you think about it?
Public Sub Init()
Dim values() As Integer = CType([Enum].GetValues(GetType(MyEnum)), Integer())
For i As Integer = 0 To values.Count - 1
Me.contextMenuInGUI.Items.Add(Me.GetEnumDescription(i))
Next
End Sub
Private Function GetEnumDescription(ByVal i As Integer) As String
Select Case i
Case MyEnum.Comment
Return "Description for Comment"
Case MyEnum.SomeEnumValueInCamelCase
Return "Value without camel case (€)(%)(#)"
End Select
Return "Add a case in Class:GetEnumDescription"
End Function
Create an extension method for your Enum
Usage example:
dim description = TableTag.Important.GetDescription()
Definition example:
Imports System.ComponentModel
Imports System.Reflection
Imports System.Runtime.CompilerServices
Namespace Foo
Public Enum TableTag
<Description("Identifies tables that should be availible for writing as table or view to the model database")>
Important
<Description("Example for a table group that helps to select disctinct tables")>
CustomGroup
End Enum
Public Module TableTagExtensions
<Extension>
Public Function GetDescription(enumValue As TableTag) As String
Dim fieldInfo As FieldInfo = enumValue.GetType().GetField(enumValue.ToString())
Dim attributes = DirectCast(fieldInfo.GetCustomAttributes(GetType(DescriptionAttribute), False), DescriptionAttribute())
If attributes.Length > 0 Then
Return attributes(0).Description
Else
Return enumValue.ToString()
End If
End Function
End Module
End Namespace

Categories

Resources