public class MyObject
{
public static enum Type {A, B, C, D;}
public static final int ID_MAIN = 1;
public static final int ID_MAIN_UK = 2;
public static final int ID_MAIN_US = 3;
public static final int ID_SUB = 4;
// lots more constants here
public static final String DESCRIPTION_1 = "Desc Full Name";
public static final String DESCRIPTION_2 = "Desc2 Full Name";
// lots more constants here
private int id;
public MyObject(final int id)
{
this.id = id;
}
//simple getter
public int getID() { return this.id;}
// real responsibility of the class is in the following two methods
public static String getDescription()
{
switch(id)
{
case MyObject.ID_MAIN:
case MyObject.ID_MAIN_UK:
return MyObject.DESCRIPTION_1;
case MyObject.ID_SUB:
return MyObject_Description_2;
default:
// throw IllegalArgException
}
}
public static Type getType(int id)
{
switch(id)
{
case MyObject.ID_MAIN:
case MyObject.ID_SUB:
return Type.A;
case MyObject.ID_MAIN_UK:
case MyObject.ID_MAIN_US:
return Type.B;
default:
return Type.Undefined;
}
}
}
Basically, there is an ID that maps to both a description and a type. This ID is passed in during construction of the class and it should map to a set of constants already contained in the class. If the id is not part of the list of constants, an error is thrown when trying to get the description that maps to the id and an 'Unknown' type is return if the type is queried. The ID maps a description to a set of constants. The same ID maps to a certain Type (defined as an enum).
This code is pretty ugly because there are tons of constants defined at the top, which makes the switch statements pretty bloated. Is there a simple way to refactor this without changing the public interface? It seems trivially simple, but it seems pretty ugly no matter how you slice it. How can I simplify these mappings to make the code more concise?
I was thinking about representing the mappings in a text file and having a manager class that held simple containers in a hashmap. When the manager class is constructed, it would create the objects by reading the text file and map them to an ID. When the manager is queried with the ID, it would just call the corresponding get method, for instance:
class Manager
{
private HashMap<int, MyObject> objectMap;
public Manager() {} //construct the object map
public String getDescription(int id) { return objectMap.get(id).getDescription();}
public Type getType(int id) { return objectMap.get(id).getType();}
}
class DataContainer
{
private String description;
private Type type;
public DataContainer(String desc, Type type) {//set mem vars}
public String getDescription() //simple getter
public Type getType() //simple getter
}
But this solution seems too complicated. Is there a better solution, preferably one that would keep everything in one class?
You can do something like following. This would be much cleaner and manageable.
public enum Type
{
MAIN(1, "Main Description"),
MAIN_UK(2, "Main UK Description"),
//....
//Define all the types
//....
UNKNOWN(-1, "Unknown Type");
private int id;
private String description;
private Type(int id, String description)
{
this.id = id;
this.description = description;
}
public static Type getById(int id)
{
for (Type type : Type.values())
{
if (id == type.getId())
{
return type;
}
}
return Type.UNKNOWN;
}
public final int getId()
{
return id;
}
public final String getDescription()
{
return description;
}
}
public class MyObject
{
private int id;
private Type type;
public MyObject(int id)
{
this.id = id;
this.type = Type.getById(id);
}
public int getId()
{
return id;
}
public Type getType()
{
return type;
}
public String getDescription()
{
return type.getDescription();
}
}
In Java enums can have methods. For example following one accepts ID and description and provides some accessors.
public enum Type {
MAIN(1, "desc1"),
UK(2, "desc2"),
SUB(4, "desc4");
private int id;
private String desc;
Type(int id, String desc) {
this.id = id;
this.desc = desc;
}
public String getDescription() {
return desc;
}
public int getType() {
//return id;
return 1+2 + 3+ id;
}
}
You could use that to improve design.
Related
I have a enum called scenes and some classes like game and menu, I get class of the enum using id how can I do that? thats all I need. sorry my english is bad.
EDIT:
this is my code
public enum Scenes {
MENU(1, "Menu"),
GAME(2, "Game");
public static int Scene = 1;
private int id;
private String name;
private Scenes (int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
You want to do something like this? To get Scenes object based on it's id?
Scenes scene = Scenes.valueOf(1)
Then that's how you do it. Look at the valueOf method.
public enum Scenes {
MENU(1, "Menu"),
GAME(2, "Game");
public static int Scene = 1;
private int id;
private String name;
private Scenes (int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public static Scenes valueOf(int id) {
for (Scenes value : Scenes.values()) {
if (value.getId() == id) {
return value;
}
}
}
}
But pay attention, that calling values() repetitively is bad for performance. So you probably should build Map<Integer, Scenes> in static initialization block and save the map to a static final field.
public enum EnumCountry implements EnumClass<Integer> {
Ethiopia(1),
Tanzania(2),
private Integer id;
EnumCountry(Integer value) {
this.id = value;
}
public Integer getId() {
return id;
}
#Nullable
public static EnumCountry fromId(Integer id) {
for (EnumCountry at : EnumCountry.values()) {
if (at.getId().equals(id)) {
return at;
}
}
return null;
}
}
I have the code like above.
How can I get Enum Id using its Enum Name.
You can simply add a method like below -
public static int getId(String enumCountryName) {
return EnumCountry.valueOf(enumCountryName).getId();
}
So the complete class will be like this -
public enum EnumCountry implements EnumClass<Integer> {
Ethiopia(1),
Tanzania(2);
private Integer id;
EnumCountry(Integer value) {
this.id = value;
}
public Integer getId() {
return id;
}
#Nullable
public static EnumCountry fromId(Integer id) {
for (EnumCountry at : EnumCountry.values()) {
if (at.getId().equals(id)) {
return at;
}
}
return null;
}
public static int getId(String enumCountryName) {
return EnumCountry.valueOf(enumCountryName).getId();
}
}
It is as simple as calling its getId() method:
Ethiopia.getId()
Or:
Tanzania.getId()
Or, assuming you meant you have the string "Ethiopia", then you can also do EnumCountry.valueOf("Ethiopia").getId(). Hope that answers your question!
You can't because their types are incompatible - i.e. String vs Integer. On the other hand, you can add a method that returns a String that combines name and id:
public enum EnumCountry implements EnumClass<Integer> {
Ethiopia(1),
Tanzania(2); // replaced comma with semicolon
private Integer id;
// ...
public String getNameId() {
// returns "Ethiopa 1"
return name() + " " + id;
}
// ...
}
If name is present as String, simply do this,
int getId(String name){
EnumCountry country = EnumCountry.valueOf(name);
return country.getId();
}
I'm using DynamoDB and I would like to store the enum's String values instead of the enum itself.
For instance, I have this enum:
public enum Source {
BREACH("breach"),
LEAKAGE("leakage");
private final String desc;
Source(String desc) { this.desc = desc; }
public String desc() { return desc; }
}
...and this "entity":
#DynamoDBTable(tableName = "Alerts")
public final class Alert implements Serializable {
private static final long serialVersionUID = 4012517315640518044L;
#DynamoDBHashKey(attributeName = "AlertId") // Partition Key or Hash Attribute
private String alertId;
#DynamoDBTypeConvertedEnum
#DynamoDBAttribute(attributeName = "Source")
private Source type;
// Constructor(s), Getter(s), Setter(s), ToString, etc...
}
With the #DynamoDBTypeConvertedEnum annotation, the value that gets saved is BREACH, but I want breach.
{
"AlertId": { "S": "a083168d-cb23-4ec8-ab80-a1c16955c4b8" },
"Source": { "S": "BREACH" },
...
"CreatedAt": { "S": "2017-05-03T14:07:36.395Z" }
}
Any clues? I did try "converters" (not fully, I couldn't make it work though) but I think I have to end up doing one for each enum type since they are all different.
You can code the Alert class like this i.e. define the attribute as String and design the getter and setter to send/receive enum object (i.e. Source).
Alert class:-
#DynamoDBTable(tableName = "Alerts")
public final class Alert implements Serializable {
private static final long serialVersionUID = 4012517315640518044L;
private String alertId;
private String type;
#DynamoDBHashKey(attributeName = "AlertId")
public String getAlertId() {
return alertId;
}
#DynamoDBAttribute(attributeName = "Source")
public Source getType() {
if (type != null)
return Source.valueOf(type);
else
return null;
}
public void setAlertId(String alertId) {
this.alertId = alertId;
}
public void setType(Source type) {
this.type = type.desc();
}
}
Create Alert:-
Stores the value as expected on database table. The get item from DynamoDB table also works fine.
public Boolean createAlert(String alertId, Source source) {
DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(dynamoDBClient);
Alert alert = new Alert();
alert.setAlertId(alertId);
alert.setType(source);
dynamoDBMapper.save(alert);
return true;
}
Override toString() this should work.
public enum Source {
BREACH("breach"),
LEAKAGE("leakage");
private final String desc;
Source(String desc) { this.desc = desc; }
public String desc() { return desc; }
#Override
public String toString() { return desc; }
}
I have a object structure like the following:
public class Product {
int id;
String name;
Size[] sizes;
boolean organic;
}
public class Size {
int id;
String value;
#JsonIgnore String info;
}
While parsing the JSON for the Product class, I want to set the info for each of the sizes to the String "Organic". In the setter for the organic property I check the value and iterate over the sizes, setting the info for each.
#JsonProperty("organic")
public void setOrganic(boolean organic) {
this.organic = organic;
if (organic)
for(Size size : sizes) size.info = "Organic";
}
First, this approach seems to be brittle as it depends on the order of properties in the JSON and secondly, it doesn't always seem to work. For a production environment, where JSON has a lot more properties, I seem to be able to set the properties on the sub-object (Size here) and read and log them during parsing but when I read it from the final deserialized object, those values are always null. Again, this behavior seems to be different for the smaller test cases I set up to test.
Does anyone know of a better way to do this ?
The appropriate place to do this would be outside of these classes and some place where this type of business logic is more appropriate.
You could create a Builder class that allows you to set all of the properties for the resulting object and when the build() method that constructs the final object is called, set any additional values as appropriate. You would apply the Jackson annotations to the Builder class then, and apply any validation to it instead of the class that it creates. This way, you guarantee that any instance of a Product would be complete and valid.
If you take my original suggestion and move the logic into a business layer of your application then you would simply pass the Builders to the appropriate method, check the value of organic on the Product.Builder, and then iterate over the Size.Builder list and change their info values appropriately.
Using the Builders to hold the logic might look something like this (the logic you're looking for is all the way at the bottom):
public class Size {
private final int id;
private final String value;
private final String info;
public Size(int id, String value, String info) {
this.id = id;
this.value = value;
this.info = info;
}
public int getId() {
return id;
}
public String getValue() {
return value;
}
public String getInfo() {
return info;
}
public static class Builder {
private int id;
private String value;
private String info;
public Builder setId(int id) {
this.id = id;
return this;
}
public Builder setValue(String value) {
this.value = value;
return this;
}
#JsonIgnore
public Builder setInfo(String info) {
this.info = info;
return this;
}
public Size build() {
return new Size(id, value, info);
}
}
}
public class Product {
private final int id;
private final String name;
private final Size[] sizes;
private final boolean organic;
public Product(int id, String name, Size[] sizes, boolean organic) {
this.id = id;
this.name = name;
this.sizes = sizes;
this.organic = organic;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public Size[] getSizes() {
return sizes;
}
public boolean isOrganic() {
return organic;
}
public static class Builder {
private int id;
private String name;
private List<Size.Builder> sizeBuilders;
private boolean organic;
public Builder setId(int id) {
this.id = id;
return this;
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setSizeBuilders(List<Size.Builder> sizeBuilders) {
this.sizeBuilders = sizeBuilders;
return this;
}
public Builder setOrganic(boolean organic) {
this.organic = organic;
return this;
}
public Product build() {
if (organic) {
for (Size.Builder sizeBuilder : sizeBuilders) {
sizeBuilder.setInfo("Organic");
}
}
Size[] sizes = new Size[sizeBuilders.size()];
for (int i = 0; i < sizeBuilders.size(); i++) {
sizes[i] = sizeBuilders.get(i).build();
}
return new Product(id, name, sizes, organic);
}
}
}
How to assign value to this function in Java incompatible types?
public class CustomerInfo implements Serializable {
private static final long serialVersionUID = 9083257536541L;
protected String id;
protected String searchkey;
protected String taxid;
protected String name;
protected String postal;
/** Creates a new instance of UserInfoBasic */
public CustomerInfo(String id) {
this.id = id;
this.searchkey = null;
this.taxid = null;
this.name = null;
this.postal = null;
}
public String getId() {
return id;
}
public String getTaxid() {
return taxid;
}
public void setTaxid(String taxid) {
this.taxid = taxid;
}
public String getSearchkey() {
return searchkey;
}
public void setSearchkey(String searchkey) {
this.searchkey = searchkey;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPostal() {
return postal;
}
public void setPostal(String postal) {
this.postal = postal;
}
public String printTaxid() {
return StringUtils.encodeXML(taxid);
}
public String printName() {
return StringUtils.encodeXML(name);
}
#Override
public String toString() {
return getName();
}
}
private CustomerInfo selectedCustomer;
public CustomerInfo getSelectedCustomer() {
// construct a CustomerInfo from the data in your String
return selectedCustomer;
}
private void jcmdOKActionPerformed(java.awt.event.ActionEvent evt) {
selectedCustomer = (CustomerInfo) jListCustomers.getSelectedValue();
//test code
String testing = m_jtxtName.getText();
System.out.println("Now the selectedCustomer is dispayed!");
System.out.println(selectedCustomer);
System.out.println(testing);
//test code
dispose();
}
In the above shown code, I need the string testing value to be assigned to selectedCustomer. How can I assign the value? This is the error I get:
selectedCustomer = m_jtxtName.getText();
incompatible types
required: CustomerInfo
found: String
You can't!!!
selectedCustomer is an object of type CustomerInfo.
m_jtxtName.getText() returns a String
You can't assign a String to a CustomerInfo.
Probably you need to do something like:
int id = 1; //Or whatever new id you have.
String name = m_jtxtName.getText();
selectedCustomer = new CustomerInfo(name); //or whatever id you have.
selectedCustomer.setName(name); //or whatever name you have.
EDIT:
Something is missing from your class. Either it needs setter methods (it has only getters now, so you can't set other properties as name etc) or it needs a constructor with four arguments like:
public CustomerInfo(String id, String searchKey, String taxid, String name, String postal) {
this.id = id;
this.searchKey = searchKey;
// etc
In this case, you might have six jtextfields in your screen, so te user can fill all fields and the create the Customerinfo object by passing all parameters to the constructor.
you cannot do it by simply casting a String to a CustomerInfo object, but you could extend your CustomerInfo but you could try something like this:
public class CustomerInfo {
[...]
public static CustomerInfo createCustomerInfo(String data) {
// construct a CustomerInfo from the data in your String
return createdCustomerInfo;
}
}
I don't know what data you have in that String so i can not give you an advice how to implement this. e.g. If it is the ID you could use this to retrieve the CustomerInfo from database or something like that.