Java Reflection - listing properties (getters & setters) of a class - java

public class foo
{
private String _name;
private String _bar;
public String getName() {
return _name;
}
public void setName(String name) {
_name = name;
}
public String getBar() {
return _bar;
}
public void setBarn(String bar) {
_bar = bar;
}
}
If I have the above class can I use reflection to list the properties defined by the getters and setters? I've tried the method below but it doesn't work, Field[] fields is left empty. I know I can do this in .Net but Java is a very different animal. Am I barking up the wrong tree altogether?
private HashMap<String, String> getHashMap(Object obj) {
HashMap<String, String> map = new HashMap<String, String>();
Class<?> cls = obj.getClass();
Field fields[] = cls.getFields();
for(Field f : fields) {
String name = f.getName();
String value = f.get(obj).toString();
map.put(name, value);
}
return map;
}
Also setters and getters maybe evil, should I just drop this?

Maybe use cls.getDeclaredFields instead ? (And f.setAccessible(true) before get private field).
If you want getter and setter you have to get method by getDeclaredMethods. Then I suggest using BeanUtils instead of writing your own reflection logic :) (IMHO less convenient is java.beans.Introspector).

Use the Introspector class. Obtain the BeanInfo and use getPropertyDescriptors() method. That should get you on the way.

You can do something like this:
List<Method> methods = Arrays.asList(getClass().getDeclaredMethods());
for (Method m : methods)
{
String name = m.getName();
if (name.startsWith("get") || name.startsWith("is"))
{
// Do something with the getter method
} else if (name.startsWith("set"))
{
// Do something with the setter method
}
}

Related

How to call class method by java annotation name with Jackson?

Wondering if there is a way to call the getter methods by the Jackson annotation property name (eg. "value") instead of the method name (eg. getName()) or point me to the right direction?
public class Person {
private String name;
#JsonProperty("value")
public String getName() {
return name;
}
#JsonProperty("value")
public void setSet(String name) {
this.name = name;
}
}
My goal is to call multiple methods by iterating trough a list of java annotation property names.
If you really want to identify and call the methods directly you could use reflection. Something like (with no exception management):
SomeObject object = ...;
Class<?> type = object.getClass();
for (Method method : type.getMethods()) {
JsonProperty property = method.getAnnotation(JsonProperty.class);
if (property != null && property.value().equals("value")) {
if (method.getParameterCount() == 0) {
Object value = method.invoke(object);
...
}
}
}
This is what I used as an answer by Allen D.
Map<String,Object> map = new ObjectMapper.convertValue(person, new TypeReference<Map<String,Object>>(){});
String s = (String) map.get("value");

Java self-reflection

It's rather strange, but I want to call self method.
This is my abstract class
public abstract class AbstractMapper {
public AbstractMapper(Map<String, String> map) {
Field[] fields = this.getClass().getDeclaredFields();
for (Field field: fields) {
if (field.getAnnotation(Column.class) != null) {
String fName = field.getName();
String rsName = field.getAnnotation(Column.class).name();
StringBuilder sb = new StringBuilder("set")
.append(Character.toUpperCase(fName.charAt(0)))
.append(fName.substring(1));
String mName = sb.toString();
// this.invoke(mName, map.get(fName)); <-- What should I put this here?
}
}
}
public Result getCalculatedValues() {
return xxxx;
}
}
And this is my class
public class NewMachine extends AbstractMapper{
#column(name = machine)
private String machine;
#column(name = temperature)
private Double temperature;
// normal get/set methods
}
Now, my goal is that AbstractMapper constructor iterates through all fields with columns, and invoke all of its respective setters.
in this case, I can pass something like
Map<String, String> map = SomeClass.SomeMethod();
NewMachine m = new NewMachine(map);
Result r = m.getCalculatedValues();
Thank you for helping.
Try getClass().getMethod( mName, field.getType() ).invoke(this, map.get(fName) ) (and handle any possible exceptions ofc).
Additionally keep the JavaDoc on getDeclaredFields() in mind:
Returns an array of {#code Field} objects reflecting all the fields declared by the class or interface represented by this Class object. This includes public, protected, default (package) access, and private fields, but excludes inherited fields.
If you have a hierarchy you'd have to get the fields of the super classes as well.

Get attribute using annotation value

I want to use the method execute() of the following class:
public class Parser {
#Header("header1")
private String attribute1;
#Header("header2")
private String attribute2;
#Header("header3")
private String attribute3;
#Header("header4")
private String attribute4;
public String execute(String headerValue) {
//Execute
}
}
What I want this method to achieve is matching the headerValue parameter with one in the list of #Header annotations, and returning the value of the respective attribute. For example, if I call execute("header3"), it should return the value of attribute3
How can I achieve this? Or is it a better way to code this requirement?
Why don't you just use a map for this? You'd need one anyways in order to store the mapping of the annotation parameter value to the field but if you can do this without reflection it should be easier to code and to maintain.
What I mean is:
Map<String, String> attributes; //initialized
attributes.put("header1", value1);
...
In execute() you then just access the map.
You could improve this using an enum, e.g. in order to restrict the number of possible values.
Something like this:
enum HeaderType {
HEADER1,
HEADER2,
...
}
private Map<HeaderType, String> headerAttribs = ...;
void setAttrib( HeaderType type, String value ) {
headerAttribs.put(type, value);
}
String getAttrib( HeaderType type ) {
return headerAttribs.get(type);
}
public String execute(HeaderType type ) {
//Execute
}
If you need to use a string for the header type you could consider employing an additional map string->header type to look up the correct type first.
Alternatively you could use a switch statement which since Java 7 should work with strings as well.
Try this:
public String execute(String headerValue) throws IllegalArgumentException, SecurityException, IllegalAccessException, NoSuchFieldException {
for(Field field:this.getClass().getFields()) {
if (field.isAnnotationPresent(Header.class)) {
Header annotation = field.getAnnotation(Header.class);
String name = annotation.value();
if(name.equals(headerValue)) {
Object val = this.getClass().getField(name).get(this);
return (String) val;
}
}
}
return null;
}
There are a couple of exception to handle in line:
Object val = this.getClass().getField(name).get(this);
You can return null for that exception if you don't want to throw it from this method.
This may help you
Field f[]= Parser.class.getDeclaredFields();
for (int i = 0; i < f.length; i++) {
Annotation annotation[]= f[i].getAnnotations();
for (int j=0;j<annotation.length;j++){
Class<Annotation> type = (Class<Annotation>) annotation[j].annotationType();
for (Method method : type.getDeclaredMethods()) {
if(method.getName() .equals(headerValue))
{
String name=f[i].getName();
return name;
}
}
}
}
Parser.class.getDeclaredFields() will include private fields also.

How to use string to compare with field names in java?

I have a class named MyClass. It has many fields of type MyField. How do I return a reference to a particular field whose name matches a String's value?
public class MyClass{
public MyField field1;
public MyField field2;
public MyField field3;
public MyField whichField(String nameOfField){
//e.g. String = "field3", then return field3
//of course I can do if else, but it will be tedious If I have long list of MyField fields, can I iterate over all field names, and return whose name matches?
}
}
edit
I tried reflection from the answers below, I create a temp placeholder, and I wish to reutrn it but,
MyField temp = MyClass.class.getDeclaredField(whichFieldString);
doesnt work, I get type mismatch, cant convert error
How do I cast this?
How do I return this field?
As an alternative:
If all fields are of the same type and are accessed by their field name (most of the time) you could avoid the hassle and brittleness of using reflection by utilizing a Map.
The map associates a key (in your case the "field name") with a value. Instead of an arbitrary number of fields, MyClass would look like:
public class MyClass {
private final Map<String, MyField> fields = new HashMap<>();
/* code to initially fill the map */
public MyField whichField(String fieldName) {
return fields.get(fieldName);
}
}
You can do this with reflection. Class A has the fields we want to search through:
public class A {
private String field1;
private String field2;
private String field3;
}
And B shows how to iterate over the fields declared in A, matching on a particular field name:
public class B {
public B() {
Field field = findFieldByName("field1");
System.out.println(field);
}
private Field findFieldByName(String name) {
Field[] fields = A.class.getDeclaredFields();
for(Field f : fields) {
if(f.getName().equals(name)) {
return f;
}
}
return null;
}
public static void main(String[] args) {
new B();
}
}
You'll have to use reflection:
import java.lang.reflect.Field;
public class MyClass {
public MyField field1;
public MyField field2;
public MyField field3;
public MyField whichField(String nameOfField) {
MyField fieldName = null;
Field[] fields = MyClass.class.getDeclaredFields();
for (Field field : fields) {
if (field.getName().equals(nameOfField)) {
// Do whatever you want to do
}
}
return null;
}
}
class MyField {
}
You may want to use a collection, e.g. Map<String, MyField>.
You can do it easily with reflection
Class<MyClass> clazz = MyClass.class;
Field requieredField = clazz.getDeclaredField("myFielldName");
EDIT
This solution is pertinent is the number of fields is fixed. As it was mentioned in comments and answers, if you want to store a dynamic number of values, then a Map (or a Collection if you only need to enumerate the values) is much more suitable.

Handling meta of class (reference to properties)

I am trying to set up a small metamodel in order to reference some properties on multiple classes.
Example: Using the classes below, I'd like to store only Person.name and Person.surname in MetaManager.config. The problem is, I don't want to store the values of name and surname, but a reference to the field. By storing these references of the field, later on I can retrieve the name and surname of any instance of Person I would pass to MetaManager.getValues().
This code is similar to Metamodel API, though I am not sure whether I should use this (since Metamodel is part of persistence and this is not related to persistence). In this API the reference is made like this Person_.name using the EntityType object.
The question is, in what way can I store a reference to these properties so I can retrieve the value of these properties from an instance later on?
The code below gives a sketch of what I'm trying to accomplish. As you can see, my problem is in Person.getValue() and a toString() on this reference (a reference on ssn would thus return "ssn").
interface IMetable {
Object getValue(Meta meta);
}
class Person implements IMetable {
String ssn;
String name;
String surname;
Person(String ssn, String name, String surname) {
this.ssn = ssn;
this.name = name;
this.surname = surname;
}
#Override
Object getValue(ClassMeta meta) {
// Return the value of the (by meta) referenced field
return null;
}
}
class MetaManager {
Map<Class, Meta[]> config;
public Map<String, String> getValues(IMetable object) {
if(config.containsKey(object.class)) {
ClassMeta[] metamodel = config.get(object.class);
Map<String, String> values = new HashMap();
for(Meta meta : metamodel) {
values.put(meta.toString(), object.getValue(meta).toString());
}
return values;
}
else {
throw new Exception("This class has not been configurated.");
}
}
}
You appear to be trying to recreate the reflection API.
Why wouldn't you just implement MetaManager like this:
public class MetaManager
{
public Map<String, Object> getValues(Object object)
{
Map<String, Object> values = new HashMap<String, Object>();
for (Field field : object.getClass().getFields())
{
boolean wasAccessible = field.isAccessible();
try
{
field.setAccessible(true);
values.put(field.getName(), field.get(object));
}
finally
{
field.setAccessible(wasAccessible);
}
}
return values;
}
}
If you need a subset of fields then use an Annotation to mark those fields and then check for that Annotation before adding it to the values map.

Categories

Resources