I have a question regarding reflection in Java.
Following problem:
Depending on a configuration I want to call a method via reflection, but not only of a class CLASS_A, but also from a class CLASS_B that is referenced by CLASS_A.
But I want to use always only class CLASS_A to access the attribute.
Here an example what I mean:
public class Foo
{
private String _name;
private Bar _bar;
public Foo(String name, Bar bar)
{
_name = name;
_bar = bar;
}
public String getName()
{
return _name;
}
public Bar getBar()
{
return _name;
}
}
public class Bar
{
private String _name;
public Bar(String name)
{
_name = name;
}
public String getName()
{
return _name;
}
}
I want to use always an instance of class Foo to invoke the method that is returned by getMethod ... no matter whether the method of Foo should be called or the method of Bar.
public class Executor
{
public static void main(String[] args)
{
Foo foo = new Foo("fooName", new Bar("barName"));
String attribute = "barName";
Method method = getMethod(Foo.class, attribute);
try
{
System.out.println(String.valueOf(method.invoke(foo, new Object[]{})));
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
private static Method getMethod(Class< ? > clazz, String attribute)
{
try
{
if (attribute.equals("fooName"))
{
return clazz.getDeclaredMethod("getName", new Class[] {});
}
else if (attribute.equals("barName"))
{
//Is that somehow possible?
Method method = clazz.getDeclaredMethod("getBar.getName", new Class[] {});
return method;
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
return null;
}
}
Is something like that possible?
Thanks!
You can use Apache BeanUtils library.
Here is a good example of how you can use it.
Related
I have a class as follows:
public class MyClass {
#JsonProperty("my_id")
private String id;
#JsonProperty("my_list")
private List<SecondClass> myList;
public getId() {
return this.id;
}
public setId(String id) {
this.id = id;
}
public getMyList() {
return this.myList;
}
public setMyList(List<SecondClass> myList) {
this.myList = myList;
}
}
My class has a dependency on another class called SecondClass [through the List entity]
public class SecondClass {
#JsonProperty("my_name")
private String name;
public getName() {
return this.name;
}
public setName(String name) {
this.name = name;
}
}
I know how to access the getters and setters of "MyClass" using Reflection based on the JsonProperty name, as shown below:
public void myMethod(MyClass myClass, String jsonProperty, String newId) throws IllegalAccessException {
for (Field field : MyClass.class.getDeclaredFields()) {
JsonProperty jsonPropAnnotation = field.getAnnotation(JsonProperty.class);
if (jsonPropAnnotation != null)
if (jsonPropAnnotation.value().equals(jsonProperty)) {
field.setAccessible(true);
field.set(myClass, newId);
}
}
}
But, my question is, is there a way to fetch the getter and setter from SecondClass via MyClass based on the JsonProperty name using Reflection?
As an example I would like to call getList() based on JsonProperty value "my_list" and then setName() based on the JsonProperty value "my_name".
Is this a possibility using reflection?
I am not sure what you want to achieve and how you will pass the values to set but all this is possible. Please check below code piece for your problem.
If you can explain me the full requirement then may be I can help you in implementing the best solution but for this ask below code will also work -
public class Main {
public static void main(String[] args) throws IllegalAccessException {
MyClass myClass = new MyClass();
List<SecondClass> secondClasses = new ArrayList<>();
myClass.setId("1234");
SecondClass sc1 = new SecondClass();
sc1.setName("Name1");
secondClasses.add(sc1);
myMethod(myClass, "my_id", "1234");
System.out.println(myClass.getId());
for (SecondClass secondClass : secondClasses) {
SecondClass secondClass1 = new SecondClass();
myMethod(secondClass1, "my_name", secondClass);
System.out.println(secondClass1.getName());
}
}
public static void myMethod(Object object, String jsonProperty, Object value) throws IllegalAccessException {
for (Field field : object.getClass().getDeclaredFields()) {
JsonProperty jsonPropAnnotation = field.getAnnotation(JsonProperty.class);
if (jsonPropAnnotation != null)
if (jsonPropAnnotation.value().equals(jsonProperty)) {
if (field.getType().equals(String.class) || field.getType().equals(Double.class)) {
field.setAccessible(true);
if (value.getClass().equals(String.class)) {
field.set(object, value);
}else{
field.set(object, field.get(value));
}
}
}
}
}
}
I have an existing class, that I want to keep ignorant of javafx. Is there a generally accepted way to decorate or adapt it so that it supports javafx properties?
I want to do something like the following (which is obviously wrong):
public class FooWrapper {
/**
* Decorates a Foo instance with javafx stuff
*/
private final Foo foo;
public FooWrapper(Foo toWrap) {
this.foo = toWrap;
}
private final StringProperty name = new SimpleStringProperty();
public final StringProperty nameProperty() {
return this.foo.getName();//?????
}
public final String getName() {
return this.nameProperty().get();
}
public final void setFirstName(final String name) {
this.nameProperty().set(name);
}
}
public class Foo {
/**
* Basic class I want to keep ignorant of javafx
*/
private String name = "hello";
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
Use the classes in the javafx.beans.property.adapter package.
public class Foo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class FooAdapter {
private final JavaBeanStringProperty name;
public FooAdapter(Foo foo) {
try {
name = JavaBeanStringPropertyBuilder.create().bean(foo).name("name").build();
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
public final void setName(String name) {
this.name.set(name);
}
public final String getName() {
return name.get();
}
public final StringProperty nameProperty() {
return name;
}
}
The adapter property, as created above, requires that the underlying object follows the Java Bean convention for properties. However, there are ways to customize what methods to use for getters/setters.
The adapter property will get the value from the underlying object and, if writable, also write to the underlying object when updated. It can also observe the underlying object for changes if it supports PropertyChangeListeners. Note that this functionality is implemented using reflection; if you are using modules you need to add the appropriate exports/opens directives to your module-info (see the javadoc of the various properties, such as JavaBeanStringProperty, for details).
Say I have class AccountPojo and GetAccountPojo with its setter and getter methods as below.
public class AccountPojo {
private String dataList;
private String dataSet;
public String getDataList() {
return dataList;
}
public void setDataList(String dataList) {
this.dataList = dataList;
}
public String getDataSet() {
return dataSet;
}
public void setDataSet(String dataSet) {
this.dataSet = dataSet;
}
}
public class GetAccountsPojo {
private String accountId;
private int noOfAccounts;
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public int getNoOfAccounts() {
return noOfAccounts;
}
public void setNoOfAccounts(int noOfAccounts) {
this.noOfAccounts = noOfAccounts;
}
}
Now I have class Test as below
public Class Test {
public static void main(String[] args) {
Class cls = Class.forName("com.org.temp."+ClassName); // ClassName(AccountPojo/GetAccountPojo) here I know already which class is getting called.
Object clsInstance = (Object) cls.newInstance();
System.out.println("The cls is==" + cls+" and classInstance is=="+clsInstance);
// Here I want to access getter and setter methods of AccountPojo and GetAcoountPojo dynamically, no hard coding
}
}
Have you tried getting all the methods of the invoked class and filtering out only the getter methods by name and invoking them?
Method[] methods = cls.getClass().getDeclaredMethods();
for (Method m: methods) {
if(m.getName().startsWith("get")) {
m.invoke(clsInstance);
}
}
This solves our half problem, as getters are invoked without any arguments. But if you need to invoke a setter method you need to specify arguments. Ex, To invoke a setter which accepts string argument method as below:
m.invoke(clsInstance, "some string argument");
One solution to could be make all the setters accept an object type value and typecast them while assigning it to actual class variables.
Now your pojo classes will look as below:
public class AccountPojo {
private String dataList;
private String dataSet;
public String getDataList() {
return dataList;
}
public void setDataList(Object dataList) {
this.dataList = (String) dataList;
}
public String getDataSet() {
return dataSet;
}
public void setDataSet(Object dataSet) {
this.dataSet = (String)dataSet;
}
}
public class GetAccountsPojo {
private String accountId;
private int noOfAccounts;
public String getAccountId() {
return accountId;
}
public void setAccountId(Object accountId) {
this.accountId = (String) accountId;
}
public int getNoOfAccounts() {
return noOfAccounts;
}
public void setNoOfAccounts(Object noOfAccounts) {
this.noOfAccounts = (int) noOfAccounts;
}
}
Add below code to your main method:
for (Method m: methods) {
if(m.getName().startsWith("get")) {
m.invoke(clsInstance);
}
if(m.getName().startsWith("set")) {
m.invoke(clsInstance, "any argument to be passed here");
}
}
Don't use raw class. If you know which class is called already, use typed class.
try {
AccountPojo obj = AccountPojo.class.newInstance();
Method setDataList = AccountPojo.class.getMethod("setDataList");
setDataList.setAccessible(true); // This is important if you want to access protected or private method. For public method you can skip
setDataList.invoke(obj, "123");
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Is an enum able to store references to a getter method, using a Supplier?
To be use like that :
String value = myEnum.getValue(object)
I can't figure how to write it without compiling errors.
If I get you right then you want to do something like this:
import java.util.function.DoubleSupplier;
public class Test {
enum MathConstants {
PI(Test::getPi), E(Test::getE);
private final DoubleSupplier supply;
private MathConstants(DoubleSupplier supply) {
this.supply = supply;
}
public double getValue() {
return supply.getAsDouble();
}
}
public static void main(String... args) {
System.out.println(MathConstants.PI.getValue());
}
public static double getPi() {
return Math.PI;
}
public static double getE() {
return Math.E;
}
}
It's not very difficult if the return type for all the getters is the same. Consider the following PoJo class:
public static class MyPoJo {
final String foo, bar;
public MyPoJo(String foo, String bar) {
this.foo = foo;
this.bar = bar;
}
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
public int getBaz() {
return 5;
}
}
Then we may have such enum:
public static enum Getters {
FOO(MyPoJo::getFoo), BAR(MyPoJo::getBar);
private final Function<MyPoJo, String> fn;
private Getters(Function<MyPoJo, String> fn) {
this.fn = fn;
}
public String getValue(MyPoJo object) {
return fn.apply(object);
}
}
And use it like this:
System.out.println(Getters.FOO.getValue(new MyPoJo("fooValue", "barValue"))); // fooValue
However it would be problematic if you want to return different types. In this case I'd suggest to use normal class with predefined instances instead of enum:
public static final class Getters<T> {
public static final Getters<String> FOO = new Getters<>(MyPoJo::getFoo);
public static final Getters<String> BAR = new Getters<>(MyPoJo::getBar);
public static final Getters<Integer> BAZ = new Getters<>(MyPoJo::getBaz);
private final Function<MyPoJo, T> fn;
private Getters(Function<MyPoJo, T> fn) {
this.fn = fn;
}
public T getValue(MyPoJo object) {
return fn.apply(object);
}
}
Usage is the same:
System.out.println(Getters.FOO.getValue(new MyPoJo("fooValue", "barValue"))); // fooValue
System.out.println(Getters.BAZ.getValue(new MyPoJo("fooValue", "barValue"))); // 5
I am trying to write my own bean utils converter so that I can export my object to a plain text file
I have the main class
public class BeanUtilsTest {
public static void main(String[] args) {
try{
MyObject myObject = new MyObject();
myObject.setId(3l);
myObject.setName("My Name");
ConvertUtilsBean cub = new ConvertUtilsBean();
cub.deregister(String.class);
cub.register(new MyStringConverter(), String.class);
cub.deregister(Long.class);
cub.register(new MyLongConverter(), Long.class);
System.out.println(cub.lookup(String.class));
System.out.println(cub.lookup(Long.class));
BeanUtilsBean bub = new BeanUtilsBean(cub, new PropertyUtilsBean());
String name = bub.getProperty(myObject, "name");
System.out.println(name);
String id = bub.getProperty(myObject, "id");
System.out.println(id);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
The Long Converter
public class MyLongConverter implements Converter{
#Override
public Object convert(Class clazz, Object value) {
System.out.println("Long convert");
return value.toString()+"l";
}
}
The String Converter
public class MyStringConverter implements Converter{
#Override
public Object convert(Class clazz, Object value) {
System.out.println("String convert");
return value.toString()+":";
}
}
Finally my object
public class MyObject {
Long id;
String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The Output
String convert
My Name:
String convert
3:
I was expecting the id will go through MyLongConverter, but it seems it is still going thru the String one. Why and how can I fix this?
Please advise
Thanks
String id = bub.getProperty(myObject, "id");
Above getProperty function in BeanUtilBean class has to return String representation of the property you requested, regardless of what format the property is defined. So, it will always use String converter (MyStringConverter).
Since the destination type here is always String, MyLongConverter will never be used.
Instead, MyStringConverter should inspect the type of the value parameter and accordingly convert it to String.