how to get make object of enum in reflection in java - java

I need get name from Enum with reflection of java.
I know I can that for class: (but I could not find it about Enums)
public class EnumWrapper<T> {
#SuppressWarnings("unchecked")
public String getType() {
Type type = getClass().getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) type;
Class<T> entity = (Class<T>) pt.getActualTypeArguments()[0];
String name = entity.getName();
return name;
}
}
I maybe set any of the Enums :
public enum Gender {
WOMAN, MAN, OTHER
}
public enum Language {
Norwegian, ENGLISH, PERSIAN
}
I want to get "Gender" or "Language" only from " getType() " method.
EnumWrapper<Gender> ew = new EnumWrapper<>();
String nameEnum = ew.getType(); // Gender

I'm not sure if this is what you want, but it follows the call style of call you indicated above:
EnumWrapper<Gender> ew = new EnumWrapper<>();
String nameEnum = ew.getType();
This Wrapper class returns the full package name of any parameter type (class or enum). It works using the variable args list, you don't need to supply any values to the constructor it as works off the empty array supplied by the VM:
public class Wrapper<T> {
private String type;
public Wrapper(T...args) {
type = args.getClass().getComponentType().getTypeName();
}
String getType() {
return type;
}
public static void main(String[] args) {
System.out.println(new Wrapper<String>().getType());
System.out.println(new Wrapper<Gender>().getType());
System.out.println(new Wrapper<Language>().getType());
}
}
... prints
java.lang.String
Gender
Language

Related

How to pass enum definition through a function and access its members there in java?

I am using mongodb and for data validation I am trying to use java enums. I have defined db schema in enum and trying to pass that enum and the actual data through the validationFunction.
Enum definition is given below.
//Enum definition
public enum Type {
STRING, OBJECT, INTEGER
}
public enum Existence {
REQUIRED, OPTIONAL
}
public enum Adress {
HOUSE_NO("houseNo", Type.STRING, Existence.REQUIRED),
STREET_NO("streetNo", Type.STRING, Existence.REQUIRED),
LANDMARK("landmark", Type.STRING, Existence.OPTIONAL)
public enum employeeSchema {
NAME("name", Type.STRING, Existence.REQUIRED),
AGE("age", Type.INTEGER, Existence.Optional),
ADDRESS("address", Type.OBJECT, Existence.REQUIRED)
String text;
Type valueType;
Existence exist;
Address(String text, Type valueType, Existence exist) {
this.text = text;
this.valueType = valueType;
this.exist = exist;
}
}
public enum employeeSchema {
NAME("name", Type.STRING, Existence.REQUIRED),
AGE("age", Type.INTEGER, Existence.Optional),
ADDRESS("address", Type.OBJECT, Existence.REQUIRED, Address)
String text;
Type valueType;
Existence exist;
employeeSchema(String text, Type valueType, Existence exist) {
this.text = text;
this.valueType = valueType;
this.exist = exist;
}
employeeSchema(String text, Type valueType, Existence exist, Enum schema) {
this.text = text;
this.valueType = valueType;
this.exist = exist;
this.schema = schema;
}
}
Now I want to pass employeeSchema through a function to validate the data.
public boolean validateData(JsonNode data, Enum schema){
//Want to iterate the enum here. Get the corresponding field from the data and will check if the field has type required and if its null it will return false. Again if the field would be object this function would be called with respective schema.
}
So the problem is, I want to pass enum through the function, but need to have generic return type because when I am collecting it in Enum type it does not have the respective enum values.
For example, if I pass employeeSchema and do employeeSchema.text it says Enum type does not have text.
I hope my problem is clear. Thanks in advance.
My ideas about your code are the following:
Make Type a class and merge it with schema field.
Make the interface like Field and make all enums implement it:
Here's the Field interface:
public interface Field {
String getText();
Type getType();
Existence getExistence();
}
Here's updated Type class:
public static final class Type {
// predefined primitive types
public static final Type INTEGER = new Type(Integer.class);
public static final Type STRING = new Type(String.class);
private final Class<?> clazz;
private Type(Class<?> clazz) {
this.clazz = clazz;
}
// Object types are created by this constructor
public static Type of(Class<? extends Field> fieldClass) {
return new Type(fieldClass);
}
#Override
public int hashCode() {
return clazz.hashCode();
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
return clazz.equals(((Type) obj).clazz);
}
public Class<?> getValueClass() {
return clazz;
}
// retrieve all possible Fields for given object type
public Field[] values() {
if(Field.class.isAssignableFrom(clazz)) {
return clazz.asSubclass(Field.class).getEnumConstants();
}
return null;
}
}
Here's your updated enums:
public enum Address implements Field {
HOUSE_NO("houseNo", Type.STRING, Existence.REQUIRED),
STREET_NO("streetNo", Type.STRING, Existence.REQUIRED),
LANDMARK("landmark", Type.STRING, Existence.OPTIONAL);
String text;
Type valueType;
Existence exist;
#Override
public String getText() {
return text;
}
#Override
public Type getType() {
return valueType;
}
#Override
public Existence getExistence() {
return exist;
}
Address(String text, Type valueType, Existence exist) {
this.text = text;
this.valueType = valueType;
this.exist = exist;
}
}
public enum employeeSchema implements Field {
NAME("name", Type.STRING, Existence.REQUIRED),
AGE("age", Type.INTEGER, Existence.OPTIONAL),
ADDRESS("address", Type.of(Address.class), Existence.REQUIRED);
String text;
Type valueType;
Existence exist;
employeeSchema(String text, Type valueType, Existence exist) {
this.text = text;
this.valueType = valueType;
this.exist = exist;
}
#Override
public String getText() {
return text;
}
#Override
public Type getType() {
return valueType;
}
#Override
public Existence getExistence() {
return exist;
}
}
So in validateData you should accept the Type and get all the possible values for this type:
public boolean validateData(JsonNode data, Type type){
Field[] values = type.values();
if(values != null) {
for(Field field : values) {
... // use field.getText()/field.getType()/etc. to validate
// probably it's ok to call recursively here
// validateData(data.get(field.getText()), field.getType());
}
} else {
Class<?> clazz = type.getValueClass();
// clazz is a simple type like Integer or String
}
}
Enum class represents a single enum constant of generic type; so NAME, in your example. What you want is something like:
public boolean validateData(JsonNode data, Class<? extends Enum> schema) {
for (Enum<?> e : schema.getEnumConstants()) {
...
}
}
And then you call
validateData(data, Adress.class);

Accessing superclass variables from an enum

Is there any way to set a variable held in an enums parent/superclass from within an enum itself? (The following doesn't compile, but illustrates what I'm attempting to achieve)....
class MyClass{
ObjectType type;
String someValue;
public void setType(ObjectType thisType){
type = thisType;
}
enum ObjectType {
ball{
#Override
public void setValue(){
someValue = "This is a ball"; //Some value isn't accessible from here
}
},
bat{
#Override
public void setValue(){
someValue = "This is a bat"; //Some value isn't accessible from here
}
},
net{
#Override
public void setValue(){
someValue = "This is a net"; //Some value isn't accessible from here
}
};
public abstract void setValue();
}
}
Then, something like so:
MyClass myObject = new MyClass();
myObject.setType(ObjectType.ball);
After doing the above, the 'someValue' string of the myObject should now be set to 'This is a ball'.
Is there any way to do this?
A nested enum type is implicitly static (see Are java enum variables static?). That includes enum types declared as inner classes, so they can't access instance fields of the outer class.
You can't do what you're trying to do with an enum, you'll have to model that as a normal class.
You could do the following, if you want MyClass.someValue to equal the someValue of the enum, but as someValue can be retrieved from the enum I'd not bother having someValue on MyClass at all, and just retrieve it from the enum when required
public class MyClass {
ObjectType type;
String someValue;
public void setType(ObjectType thisType) {
this.type = thisType;
this.someValue = thisType.getSomeValue();
}
enum ObjectType {
ball ("This is a ball"),
bat ("This is a bat"),
net ("This is a net");
private final String someValue;
ObjectType(String someValue) {
this.someValue = someValue;
}
public String getSomeValue() {
return someValue;
}
}
}

Trying to figure out Inheritance and polymorphism

I'm doing an exercise on Inheritance and polymorphism, I have 3 seperate clasees, my main class, a super Animal class, and a sub Cat class. I've made overloaded constructors, getters and setters, and toString() methods in both Animal and Cat classes. I think I have the inheritance part down. Now I need to make 2 Animal Object references, both an instance of Cat, example: one a type Siameese with a name Tobbie.
Could anyone give me an example of one of these object references? You can see I've attempted in my Main class there, but I'm not sure if that is correct.
Here are the three different classes I have currently.
public class Hw02 {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Animal Siamese = new Cat("Tobbie");
}
}
Here's my Animal Class.
public class Animal {
private String name;
public Animal() {
this("na");
}
public Animal(String name) {
this.name = name;
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Animal{"
+ "name="
+ name
+ '}';
}
}
And here is my Cat class.
public class Cat extends Animal {
private String type;
public Cat() {
}
public Cat(String type) {
this.type = type;
}
public Cat(String type, String name) {
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
#Override
public String toString() {
return "Cat{"
+ "type="
+ type
+ '}';
}
}
// in main method
Animal tobbie = new Cat("siamese", "Tobbie")
Animal jackie = new Cat("tomcat", "Jackie")
// in Cat class
public Cat(String type, String name) {
super(name)
this.type = type;
}
A few comments:
It is not proper convention to have the name Siamese; variable names should be "camelCase" (start with a lower-case letter). Compiler will accept it is as you have written, but it is a bad practice.
Your Cat(String type, String name) constructor didn't invoke the proper superclass constructor, thus type was lost; same for the Cat(String type) constructor
I think I would make Animal abstract and its constructors protected. I think it is a bad practice to let clients directly instantiate Animals without specifying what kind of animals they are.
Edit:
Like this:
Animal animal = new Animal("What am I?")
However, I don't consider it a good practice to do this, probably what you want done is better achieved otherwise.
Edit:
Cat toString():
public String toString() {
return super.toString() + " Cat{type=" + type + "}";
}
With the code you have above, this is an example:
Animal animal0 = new Cat("Siamese", "Bob");
Animal animal1 = new Cat("Tomcat", "Frank");
Animal animal2 = new Cat("Tomcat", "George");
Animal animal3 = new Animal("Elephant");
System.out.print(animal0.toString());
System.out.print(animal1.toString());
System.out.print(animal2.toString());
System.out.print(animal3.toString());
Would produce the output:
Cat{type=Siamese}
Cat{type=Tomcat}
Cat{type=Tomcat}
Animal{name=Elephant}

Casting objects via reflection in Java

I am writing a deserializer method, which looks like so:
public <T> T deserialize(Object[] result, String[] fields, Class<T> type);
So basically I will be passed in a result array of data which is all objects, and a class type T which I need to convert the data in the array to the types in the given class, and create a new class of type T and return it. The String[] fields is the field names corresponding to the data in Object[] result. The field names will correspond to the Class T.
The casting will need to use reflection of the given class to find out the type of each field.
eg.
result = ["Mike", "London", 28];
fields = ["name", "location", "age" ];
Class T =
public class GivenClass{
private String name;
private String location;
private Integer age;
public GivenClass(String name, String location, Integer age){
this.name = name;
this.location = location;
this.age = age;
}
}
Class implementation
static class GivenClass {
private String name;
private String location;
private Integer age;
public GivenClass(String name, String location, Integer age) {
this.name = name;
this.location = location;
this.age = age;
}
public GivenClass(Map<String, Object> data) throws Exception {
for (Field f : GivenClass.class.getDeclaredFields())
f.set(this, data.get(f.getName()));
}
public Map<String, Object> serialize() throws Exception {
Map<String, Object> fields = new HashMap<String, Object>();
for (Field f : GivenClass.class.getDeclaredFields())
fields.put(f.getName(), f.get(this));
return fields;
}
#Override
public String toString() {
return "age=" + age + ", location=" + location + ", name=" + name;
}
}
Example:
public static void main(String[] args) throws Exception {
GivenClass o1 = new GivenClass("Mike", "London", 28);
Map<String, Object> serialized = o1.serialize();
GivenClass o2 = new GivenClass(serialized);
System.out.println(o2.toString());
}
Output:
age=28, location=London, name=Mike
You need to do the conversion yourself. Reflections doesn't convert (it will only check the type of an object is already correct)
Reflections won't give you the names of method/constructor parameters. (You can get them from the debug byte code but that's a real pain)
The approach I take is to use the convention that the constructor parameters are in the same order as the fields. You will also want to assume the type of constructor parameters and field types match. ;)
I would also use primitives instead of wrappers whenever possible. Use int unless you want null to be a valid option. If this is the case you should think about how you want to represent this. For text I usually use empty strings or blank field for null or NaN depending on the context.
The problem with this, is that in Java it's unable to fetch the parameter names of a constructor.
For this particular example, you'll need a default constructor, with which you could create an empty object.
public GivenClass() {
super();
}
Then you could use reflection to get the fields of the class, and then set the appropriate value for them.
But I think it would be much easier to annotate your constructor, and then fetch the annotation informations in your deserialize method. In this case you won't need to fetch the fields and create an empty constructor.
Example:
You need to create a annotation like this:
#Target({ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface Property
{
String value();
}
And then you can use it in your constructor like this:
public GivenClass(#Property("name") String name, #Property("location") String location, #Property("age") Integer age) {
// ...
}
As Peter Lawrey says, casting does not convert a string into an integer.
If your bean follows the standard bean conventions (ie you have getters & setters), then you can use BeanUtils. BeanUtils does some standard conversions, and you can add more by adding a Convertor.
See the following example:
import org.apache.commons.beanutils.BeanUtils;
public class BeanUtilsTest {
public static class Obj {
private int number;
private String string;
public void setNumber(int number) {
this.number = number;
}
public void setString(String string) {
this.string = string;
}
public String toString() {
return "number=" + number + " string=" + string;
}
}
public static void main(String args[]) throws Exception {
String[] values = new String[] { "1", "two" };
String[] properties = new String[] { "number", "string" };
Obj obj = new Obj();
for (int i = 0; i < properties.length; i++) {
BeanUtils.setProperty(obj, properties[i], values[i]);
}
System.out.println("obj=" + obj);
}
}
This produces as output:
obj=number=1 string=two
Note that the above example has only setters, but still works.

Providing custom value serialization for enums via JAXB

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();
}
}

Categories