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));
}
}
}
}
}
}
Related
I created a parent class Repo which has methods for insert, delete, display and delete objects in a list. Repo is a generic class. I created a child classes for Repo (like DepartmentRepo class)and pass Department, Employee, etc.. classes. I want perform insert, delete, display and delete operations on any class objects that passed to Repo class.
I need to get the return value of the method "get" which is from Generic class in java. I can only get the method name from Generic here I mention the code files
public class Department {
private long Id;
private String Name;
private String Location;
public Department() {
}
public Department(long id, String name, String location) {
super();
Id = id;
Name = name;
Location = location;
}
public long getId() {
return Id;
}
public void setId(long id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getLocation() {
return Location;
}
public void setLocation(String location) {
Location = location;
}
}
import java.util.ArrayList;
import java.util.List;
public class Repo<T, U> {
List<T> list = new ArrayList<T>();
public List<T> getAll() {
return list;
}
public void insert(T obj) {
list.add(obj);
}
public T get(U id) throws NoSuchMethodException, SecurityException {
for (T ele : list) {
if (ele.getClass().getMethod("getId") == id) {
return ele;
}
}
return null;
}
public void delete(U id) throws NoSuchMethodException, SecurityException {
list.remove(get(id));
}
}
public class DepartmentRepo extends Repo<Department, Long>{
}
class MainApi
{
public static void main (String[] args)
{
DepartmentRepo dept = new DepartmentRepo ();
Department ict=new Department(10001,"Dept of ICT","Town");
Department cs=new Department(10002,"Dept of Computer Science","Pampaimadu");
Department bio=new Department(10003,"Dept of Bio Science","Pampaimadu");
Department sats=new Department(10004,"Dept of Statistics","Kurumankadu");
dept.insert(ict);
dept.insert(cs);
dept.insert(bio);
dept.insert(sats);
System.out.println();
dept.getAll();
try{
dept.get(10001);
}
catch(Exception e){
}
}
}
As another solution, and an answer to your comment, you could use elements inheritance, and avoid reflection calls and exceptions.
1- Create an element interface OR class
public interface RepoElement<U> {
U getId();
}
OR
public class RepoElement<U> {
private U Id;
public RepoElement() {}
public RepoElement(U id) {
Id = id;
}
public U getId() {
return Id;
}
public void setId(U id) {
Id = id;
}
}
2- Make Department inherit from the interface OR class
public class Department implements RepoElement<Long> {
(...)
public Long getId() {
return Id;
}
(...)
}
OR
public class Department extends RepoElement<Long> {
private String Name;
private String Location;
public Department() {
super();
}
public Department(long id, String name, String location) {
super(id);
Name = name;
Location = location;
}
}
3- Use it directly in the Repo class (and remove exceptions)
public class Repo<T extends RepoElement<U>, U> {
(...)
public T get(U id) {
for (T ele : list) {
if (ele.getId().equals(id)) {
return ele;
}
}
return null;
}
public void delete(U id) {
list.remove(get(id));
}
(...)
}
As a last suggestion, you could use a Map instead of a List in the Repo class, and get rid of any search complexity/optimizations:
public class Repo<T extends RepoElement<U>, U> {
Map<U, T> map = new HashMap<U, T>();
public Collection<T> getAll() {
return map.values();
}
public void insert(T obj) {
map.put(obj.getId(), obj);
}
public T get(U id) {
return map.get(id);
}
public void delete(U id) {
map.remove(id);
}
}
You need to invoke the getId() method so that it will return the id to perform comparison correctly:
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
public class Repo<T, U> {
List<T> list = new ArrayList<T>();
public List<T> getAll() {
return list;
}
public void insert(T obj) {
list.add(obj);
}
public T get(U id) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
for (T ele : list) {
if (ele.getClass().getMethod("getId").invoke(ele).equals(id)) {
return ele;
}
}
return null;
}
public void delete(U id) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
list.remove(get(id));
}
}
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();
}
Mapping an enum class in to DynamoDB object is really simple by using Custom Marshall. But how to map a List of Enum?
Enum class
public enum Transport {
SMS,EMAIL,ALL;
}
DynamoDB mapper
public class Campaign{
private List<Transport> transport;
#DynamoDBAttribute(attributeName = "transport")
public List<Transport> getTransport() {
return transport;
}
public void setTransport(List<Transport> transport) {
this.transport = transport;
}
}
DynamoDBMarshaller is deprecated.
Use DynamoDBTypeConverter instead.
Example:
Enum class
public static enum ValidationFailure {
FRAUD, GENERAL_ERROR
}
DynamoDBTable class
#DynamoDBTable(tableName = "receipt")
public class Receipt {
private Long id;
private List<ValidationFailure> validation;
#DynamoDBHashKey(attributeName = "id")
public Long getId() {
return id;
}
#DynamoDBTypeConverted(converter = ValidationConverter.class)
public List<ValidationFailure> getValidation() {
return validation;
}
public void setId(Long id) {
this.id = id;
}
public void setValidation(List<ValidationFailure> validation) {
this.validation = validation;
}
}
Convertor:
public class ValidationConverter implements DynamoDBTypeConverter<List<String>, List<ValidationFailure>> {
#Override
public List<String> convert(List<ValidationFailure> object) {
List<String> result = new ArrayList<String>();
if (object != null) {
object.stream().forEach(e -> result.add(e.name()));
}
return result;
}
#Override
public List<ValidationFailure> unconvert(List<String> object) {
List<ValidationFailure> result = new ArrayList<ValidationFailure>();
if (object != null) {
object.stream().forEach(e -> result.add(ValidationFailure.valueOf(e)));
}
return result;
}
}
It's working for me, I have used the Set
#DynamoDBTyped(DynamoDBMapperFieldModel.DynamoDBAttributeType.SS)
var roles: MutableSet<Employee.Role>? = null
I think the same approach would work for List with DynamoDBAttributeType.L
I found the answer myself. I create a custom marshall like below.
public class TransportMarshaller implements DynamoDBMarshaller<List<Transport>> {
#Override
public String marshall(List<Transport> transports) {
List<String>transportMap=new ArrayList<>();
for(Transport transport:transports){
transportMap.add(transport.name());
}
return transportMap.toString().replaceAll("\\[|\\]", "");//Save as comma separate value for the purpose of easiness to unmarshall
}
#Override
public List<Transport> unmarshall(Class<List<Transport>> aClass, String s) {
List<String>map= Arrays.asList(s.split("\\s*,\\s*")); //split from comma and parse to List
List<Transport>transports=new ArrayList<>();
for (String st:map){
transports.add(Transport.valueOf(st));
}
return transports;
}
}
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.
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.