For a project I'm working on, we have a lot of enums in use. The model object itself is composed from a lot of tiny classes; this model we then serialize to our DB as XML via JAXB. Now, we want to be able to serialize our enum values using the return of a particular method in the enum; that is given:
public enum Qualifier {
FOO("1E", "Foo type document"),
BAR("2", "Bar object");
private String code, description;
public Qualifier(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return this.code;
}
public String getDescription() {
return this.description;
}
}
etc. etc. Currently, when serialized to XML, we get something like:
<qualifier>FOO</qualifier>
which is how JAXB handles it. However, we need the value to be the return of getCode(), and a whole lot of our enums do follow that convention (with a corresponding static method for lookup via code), so that the above XML fragment looks like:
<qualifier>1E</qualifier>
instead. We can annotate it with #XmlEnum and #XmlEnumValue, but that's too tedious -- some enums have up to 30 enumerated values, and hand-editing it is not good. We're also thinking of using a custom serializer instead, but I'd like to avoid going that route for now (but if that's the way to go, then I have no problem with it).
Any ideas how?
Try using the XmlAdapter mechanism for this. You create an XmlAdapter subclass for each enum type, and which knows how to marshal/unmarshal the enum to and from XML.
You then associate the adapter with the property, e.g.
public class QualifierAdapter extends XmlAdapter<String, Qualifier> {
public String marshal(Qualifier qualifier) {
return qualifier.getCode();
}
public Qualifier unmarshal(String val) {
return Qualifier.getFromCode(val); // I assume you have a way of doing this
}
}
and then in the model classes:
#XmlJavaTypeAdapter(QualifierAdapter.class)
private Qualifier qualifier;
You can also declare this at the package level, inside a file called package-info.java in the same package as your model classes, using the rather idiosyncratic package annotations:
#javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters({
#javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(
type=Qualifier.class, value=QualifierAdapter.class
)
})
package com.xyz;
Found this question while looking for something else but I read your comment about something more generic. Heres what I have been using to convert upper case enum types to camel case. I am going to use your enum type but put my adapter on it. As you can see you dont need to reference every instance of Qualifier but just annotate the enum itself.
The CamelCaseEnumAdapter can take any enum however the enum class must be passed to it therefore you need to have a class extend it, I just use a private static class inside the enum itself.
Enum:
#XmlJavaTypeAdapter(Qualifier.Adapter.class)
public enum Qualifier {
FOO("1E", "Foo type document"),
BAR("2", "Bar object");
private String code, description;
public Qualifier(String code, String description) {
this.code = code;
this.description = description;
}
public String getCode() {
return this.code;
}
public String getDescription() {
return this.description;
}
private static class Adapter extends CamelCaseEnumAdapter<Qualifier> {
public Adapter() {
super(Qualifier.class, FOO);
}
}
}
Adapter
public abstract class CamelCaseEnumAdapter<E extends Enum> extends XmlAdapter<String, E>{
private Class<E> clazz;
private E defaultValue;
public CamelCaseEnumAdapter(Class<E> clazz) {
this(clazz, null);
}
public CamelCaseEnumAdapter(Class<E> clazz, E defaultValue) {
this.clazz = clazz;
this.defaultValue = defaultValue;
}
#Override
#SuppressWarnings("unchecked")
public E unmarshal(String v) throws Exception {
if(v == null || v.isEmpty())
return defaultValue;
return (E) Enum.valueOf(clazz, v.replaceAll("([a-z])([A-Z])", "$1_$2").toUpperCase());
}
#Override
public String marshal(E v) throws Exception {
if(v == defaultValue)
return null;
return toCamelCase(v.name());
}
private String toCamelCase(String s){
String[] parts = s.split("_");
String camelCaseString = "";
for (String part : parts){
if(camelCaseString.isEmpty())
camelCaseString = camelCaseString + part.toLowerCase();
else
camelCaseString = camelCaseString + toProperCase(part);
}
return camelCaseString;
}
private String toProperCase(String s) {
return s.substring(0, 1).toUpperCase() +
s.substring(1).toLowerCase();
}
}
Related
I have two classes where one class inherits the other one as given below:
public class UserData {
protected final String emailAddress;
protected final String name;
public UserData(final String emailAddress, final String name) {
this.emailAddress = emailAddress;
this.name = name;
}
public Optional<String> getEmailAddress() {
return Optional.ofNullable(this.emailAddress);
}
public Optional<String> getName() {
return Optional.ofNullable(this.name);
}
}
public class EmployeeData extends UserData {
protected final String designation;
public EmployeeData(
final String emailAddress,
final String name,
final String designation
) {
super(emailAddress, name);
this.designation = designation;
}
public Optional<String> getDesignation() {
return Optional.ofNullable(this.designation);
}
}
I need to create method in another class that can return either one of these objects and have all getters accessible. I already tried making the return type UserData for both kinds of objects (example given below) but that way, I cannot access the getDesignation getter for EmployeeData. Is there a better way inheritance can be setup to avoid this problem where I cannot access child-specific properties?
public UserData getData() {
if (...some condition) {
return new EmployeeData("address#provider.com", "myName", "Dev")
}
else {
return new UserData("address#provider.com", "myName");
}
}
I did look into these stackoverflow questions but couldn't quite figure it out for my use case
C# how to make a function that can return either child or parent class
What's the equivalent of C# IEnumerable in Java? The covariant-capable one, not the Iterable
Because the object we are returning is of type UserData, we will be unable to call methods that are added within the child class, EmployeeData. You could create the getDesignation() method inside the UserData class and have it return an empty optional object.
public Optional<String> getDesignation() {
return Optional.empty();
}
In this case, you can now override the method within the EmployeeData class to return designation as an Optional like this,
#Override
public Optional<String> getDesignation() {
return Optional.ofNullable(this.designation);
}
Now you will have access to the getDestination() method from returned object of getData(), but you will have to be careful and understand that if the returned type is of UserData, then when calling getDesignation() you will be receiving an Optional.empty() object.
I like to make a generic enum that accepts anything.
For this example I use TaskStatus, but in the future, I like to use Generic Enum for example; StudentStatus, this student status can take an id and description itself and it will convert automatically. Moreover, iterate every object and finally automatically return. Is there any chance I can make it?
#Getter
#AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum TaskStatusEnum{
READY(1, "Ready"),
ON_GOING (2,"On going");
private final long id;
private final String description;
public static TaskStatusEnum get (long id)
{
for (TaskStatusEnum status : TaskStatusEnum.values()) {
if (status.id == id) {
return id;
}
}
return null;
}
I'm not sure what exactly you want. You can use interface on enum, then you can use interface as status and dont care what exactly status class is.
public interface Status<E extends Enum<E> & Status<E>> {
public long getId();
public String getDescription();
}
student status:
public enum StudentStatus implements Status<StudentStatus>{
NEW(0, "new");
;
private long id;
private String description;
private StudentStatus(long id, String description) {
this.id=id;
this.description = description;
}
#Override
public long getId() {
return id;
}
#Override
public String getDescription() {
return description;
}
}
task status:
public enum TaskStatus implements Status<TaskStatus>{
OPEN(0, "open");
;
private long id;
private String description;
private TaskStatus(long id, String description) {
this.id=id;
this.description = description;
}
#Override
public long getId() {
return id;
}
#Override
public String getDescription() {
return description;
}
}
generic method to find out status by id
public abstract class StatusUtil {
public static <E extends Enum<E> & Status<E>> E get(Class<E> statusClass, long id) {
return Arrays.asList((E[]) statusClass.getEnumConstants())
.stream()
.filter(item -> item.getId() == id)
.findAny()
.orElse(null);
}
}
example how use:
public class Test {
public static void main(String... args) {
StudentStatus studentStatus = StatusUtil.get(StudentStatus.class, 0);
TaskStatus taskStatus = StatusUtil.get(TaskStatus.class, 0);
List<Status> statusList = Arrays.asList(studentStatus, taskStatus);
statusList.forEach(status -> System.out.println(status.getClass().getName()+"\t"+status.getId()+"\t"+status.getDescription()));
}
}
if you use JAVA below 8:
public interface Status<E extends Enum<E>> {
public long getId();
public String getDescription();
}
statusUtil:
public abstract class StatusUtil {
public static <E extends Enum<E>> E get(Class<E> statusClass, long id) {
for(E item: (E[]) statusClass.getEnumConstants()) {
if(item.getId() == id) {
return item;
}
}
return null;
}
}
test:
public static void main(String... args) {
StudentStatus studentStatus = StatusUtil.get(StudentStatus.class, 0);
TaskStatus taskStatus = StatusUtil.get(TaskStatus.class, 0);
List<Status> statusList = Arrays.asList(studentStatus, taskStatus);
for(Status status: statusList) {
System.out.println(status.getClass().getName()+"\t"+status.getId()+"\t"+status.getDescription());
}
}
This you can use in cases, when enums has this same methods and you need common interface
Your enum is effectively final (no subclass allowed)
Apparently you are asking if TaskStatus enum can be subclassed. For example making a StudentStatus that inherits from TaskStatus.
➥ No, enums in Java cannot be subclassed.
Your enum definition actually is a subclass of Enum. That happens in the background, magically handled by the compiler. The inheritance stops there. Your enum definition is effectively final, not allowing further subclasses.
An enum definition can implement an interface. Instances from multiple enum definitions can be treated as all being objects of the same interface. See Answer by Victor1125.
An enum in Java is a convenient way to automatically instantiate one or more name objects, to represent a limited set of values known at compile time. Those instances all pop into existence when their definition class is loaded by the Java classloader. Those objects remain in memory.
You cannot add more instances dynamically at runtime. The entire domain of the enum’s objects is defined at compile time. (Exception: Some crazy twisted reflection/introspection code may be able to create more instances, but I would not go there.)
If you want inheritance, or dynamically created instances, do not use enums. Use regular classes and subclasses, collected into sets or lists. The sets or lists can be marked (< … >) with generics to allow the superclass of their contained elements. For example Set< Animal > can contain objects of the subclasses Dog, Cat, and Bird.
By the way, you can now define an enum in 3 places: its own class, nested within another class, and now in Java 16 (previewed in Java 15), locally inside a method.
Tip: No need to put "Enum" within the name of your enum. Endeavor to invent names for your enum class and enum objects that read naturally. The fact that they happen to be an enum should fade into the background. For example: See Month (Month.JANUARY) and DayOfWeek (DayOfWeek.MONDAY).
How to handle null point on StatusUtil.class
StatusUtil:
public abstract class StatusUtil {
public static <E extends Enum<E>> E get(Class<E> statusClass, long id) {
for(E item: (E[]) statusClass.getEnumConstants()) {
if(item.getId() == id) {
return item;
}
}
return null;
}
I'll try to keep this short. I'm trying to do something like this:
public enum Fruit {
APPLE("Apple", appleHelper::doAppleThing),
ORANGE("Orange", orangeHelper::doOrangeThing);
private String name;
private Function<String, List<T>> fruitFunction;
Fruit(String name, Function<String, List<T>> fruitFunction) {
this.name = name;
this.fruitFunction = fruitFunction;
}
public String getName() {
return name;
}
public <T> List<T> applyFruitFunction(String someString) {
return fruitFunction.apply(someString);
}
}
Such that later, I can have a method like
private <T> List<T> doFruitThing(String someString, Fruit fruit) {
List<T> transformedFruits = fruit.applyFruitFunction(someString);
if (transformedFruits.isEmpty()) {
throw new FruitException("There was no fruit of type " + fruit.getName());
}
return transformedFruits;
}
There's two problems I'm running into here.
doAppleThing and doOrangeThing are not static methods, and ideally will stay that way, and I can't find any way of creating a local instance of appleHelper and orangeHelper to make the method reference work.
Even if I were to make the methods static, enums can't have Type parameters, so there's no way to have Function<String, List<T>> fruitFunction as a field.
Is there a way this can be done? Or a better approach to this?
Enum values can have their own method implementations. So I would write this as:
public enum Fruit {
APPLE("Apple") {
private final AppleHelper helper = new AppleHelper();
#Override
public <T> List<T> applyFruitFunction(String someString) {
return helper.doAppleThing(someString);
}
},
ORANGE("Orange") {
private final OrangeHelper helper = new OrangeHelper();
#Override
public <T> List<T> applyFruitFunction(String someString) {
return helper.doOrangeThing(someString);
}
};
private String name;
Fruit(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract <T> List<T> applyFruitFunction(String someString);
}
However, if you get to the point of needing per-instance state for enum instances, the thing you have is less and less an enum and more of just an abstract base class. It might be better to look into a more OO approach, using a factory/flywheel pattern for example, rather than being tied to a pure enum for this sort of thing. (It's hard to tell for sure because the code in the question is obviously just a simplified example.)
I'm working on a project that requires me to serialize and deserialize generic objects. The way I'm going about this, is defining an abstract class Serializer that implements a toBytes() and a static fromBytes(). All is well with this approach, as I can pass an object instance to a generic class Foo that expects a Serializer subclass, and I can ensure the object knows how to serialize and deserialize itself.
Now my question. Java serialization kinda sucks. I have multiple implementations I'd like to try swapping in and out, and ultimately I'd like the user to be able to decide the format. How would I go about changing the implementation details of Serializer? I know I can't override static methods, so how would I do this without decoupling Foo and Serializer and not being able to ensure my generic object has the appropriate toBytes() and fromBytes() method in Foo?
Here is code if anyone is confused:
public abstract class Serializer {
public static Serializer fromBytes(byte[] bytes) {
...
}
public byte[] toBytes() {
...
}
}
public class Foo<T extends Serializer> {
private T t;
public Foo(T t) {
this.t = t;
}
public void foo() {
t.toBytes(); //this will polymorph into the correct call because it's called on the object instance and not the Serializer class
}
public void bar(byte[] bytes) {
T.fromBytes(bytes); // I'd like to be able to override this method so I can use different implementations
}
}
I'm not sure if this is a good approach, but how about using Jackson library and serialize your object as a json node? for example:
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type")
#JsonSubTypes({
#Type(value = SoundFile.class, name = "sound"),
#Type(value = VideoFile.class, name = "video")
})
abstract class File{
private String id;
private String type;
#JsonCreator
public File(#JsonProperty("id") String id)
{
this.id=id;
}
public String getId() {return this.id;}
public abstract String getType();
}
class SoundFile extends File{
#JsonCreator
public SoundFile(#JsonProperty("id") String id) {
super(id);
}
#Override
public String getType() {
return "sound";
}
}
class VideoFile extends File{
#JsonCreator
public VideoFile(#JsonProperty("id") String id) {
super(id);
}
#Override
public String getType() {
return "video";
}
}
public class GenericApp {
public static void main(String[] args) {
ObjectMapper om = new ObjectMapper();
List<File> files = Arrays.asList(new VideoFile("1"),new SoundFile("2"));
//serialize
List<byte[]> fileSerialized = files.stream().map(file->{
try {
return om.writeValueAsBytes(file);
}catch(Exception e) {return null;}
}).collect(Collectors.toList());
//de-serialize
List<File> filesDeSerialized = fileSerialized.stream().map(bytes ->{
try {
return om.readValue(bytes, File.class);
}
catch(Exception e) {return null;}
}).collect(Collectors.toList());
filesDeSerialized.stream().forEach(file->{
System.out.println("id :"+file.getId()+" - "+file.getClass());
});
}
}
this would properly deserialize these objects and print:
id :1 - class com.dsncode.stackoverflow.VideoFile
id :2 - class com.dsncode.stackoverflow.SoundFile
however, you should define a #JsonTypeInfo and a #JsonSubType for all your sub-classes of your Generic Type. Because, by indicating this field, you will indicate to Jackson deserializer, which class should create for your generic type.
Consider below inner Enum implementing an interface:
public interface NotificationTypes {
public enum CONTACT_LIST implements NotificationTypes{
ADDED("CONTACT_LIST-ADDED"),
REMOVED("CONTACT_LIST-REMOVED");
public enum INVITATION implements NotificationTypes{
ACCEPTED("CONTACT_LIST-INVITATION-ACCEPTED"),
REJECTED("CONTACT_LIST-INVITATION-REJECTED");
String name = "";
private INVITATION(String name){
this.name = name;
}
public String getName(){
return name;
}
};
String name = "";
private CONTACT_LIST(String name){
this.name = name;
}
public String getName(){
return name;
}
}
public String getName();
}
Now consider that data in database/mongodb is stored in the form of String for NotificationTypes in a table/document.
{
"_id" : ObjectId("59882ba49e5d82c72ba44fde"),
"template" : "Contact list Invitation accepted by your friend",
"type" : "CONTACT_LIST-INVITATION-ACCEPTED"
}
So my question is: How to convert that string back into specific enum at runtime without knowing exactly the name of enum to be mapped?
Domain class is looks like this:
#Document(collection = CollectionNames.XXX_TEMPLATE)
public class XXXTemplate {
private NotificationTypes type;
//Other parameters, getters & setters + Constructor
}
I'd build a Map<String, NotificationTypes> and populate that with all instances you have. You can then look up from that map.
I don't think the compiler can help you a lot with keeping that in sync, other than that you can loop over EnumType.values() (but you have to remember to do it for all of your enum types).
How to convert that string back into specific enum at runtime without knowing exactly the name of enum to be mapped?
Via Enum.valueOf().
Basically just building on #Thilo's answer, but perhaps a more 'Springified' way if it's something that you'd want - you could define a #Bean in your config that contains all your enum values, like:
#Configuration
public class Config {
#Bean
public List<NotificationTypes> notificationTypes() {
List<NotificationTypes> notificationTypes = new ArrayList<>();
notificationTypes.addAll(Arrays.asList(NotificationTypes.CONTACT_LIST.values()));
notificationTypes.addAll(Arrays.asList(NotificationTypes.CONTACT_LIST.INVITATION.values()));
return notificationTypes;
}
}
And then #Autowire this #Bean into a parser to do the actual matching of String to enum, something like:
#Component
public class NotificationTypeParser {
#Autowired
private List<NotificationTypes> notificationTypes;
public NotificationTypes parseNotificationType(String type) {
for (NotificationTypes notificationType : notificationTypes) {
if (notificationType.getName().equals(type)) {
return notificationType;
}
}
return null;
}
}
Obviously you probably want something better than just returning null if the enum isn't found, and you could potentially do something smarter in the #Bean definition to validate that the enums all have different names, etc. Or, conceivably, use reflection in there to find all the implementations of NotificationTypes.
I'm not sure that this really gives you any additional benefits over just storing all the possible values in a Map, but, as I say, I suppose it's a bit Spring-ier.