public class MessageModel<T> {
T body;
}
Now T
could be object of type say Cricket
or Football
. Now this class is serialised and published to a queue.
While consuming, what is the best way to deserialise and identify the message type as Cricket or Football, so that the corresponding service is called. I am using jackson to serialise.
I tried introducing a new property Type
during serialisation and during deserialisation will first read as json object, identify the Type and again use jackson to deserialise as the type is known.
Ex:
JSONObject jsonObject = new JSONObject(message);
String messageType = jsonObject.optString("message_type");
objectMapper.readValue(message,new TypeReference<MessageModel<messageType>>(){});
But looking for better approaches.
While consuming, what is the best way to deserialise and identify the message type as Cricket or Football, so that the corresponding service is called.
This isn't possible. Generics don't work that way.
Generics are involved only at compile-time. When your program runs, the type information from generics is no longer present. There is great information and examples in the Java Tutorial on generics.
As a brief example, here are two simple classes:
class MessageModel<T> {
}
class Cricket {
}
If we create a new MessageModel<Cricket>
, it compiles and runs fine.
MessageModel<Cricket> cricket = new MessageModel<>();
But at runtime, cricket
is identified only as an instance of MessageModel
– there is no evidence that the Cricket
class was associated in any way. As a bit of evidence, asking the cricket
object to report its own class at runtime it will print itself as MessageModel
:
System.out.println("runtime: " + cricket.getClass());
runtime: class MessageModel
This behavior is within a single JVM, but nothing changes if your program serializes an object and sends it to some other program. That other program also has no ability to determine the original Cricket
generic type from the serialized object.
If you need the original class later, you could do something like below, capturing the original class at object construction. Later, when the receiving program needs to know Cricket
was the original class, it can call getConcreteClass()
:
First, modify MessageModel
: require a Class
at object construction, save that class as a private member, add getConcreteClass()
to expose it:
class MessageModel<T> {
private final Class concreteClass;
MessageModel(Class concreteClass) {
this.concreteClass = concreteClass;
}
public Class getConcreteClass() {
return concreteClass;
}
}
From there, the original class is avaiable at runtime:
MessageModel<Cricket> cricket = new MessageModel<>(Cricket.class);
System.out.println("getClass() : " + cricket.getClass());
System.out.println("getConcreteClass() : " + cricket.getConcreteClass());
getClass() : class MessageModel
getConcreteClass() : class Cricket