I have an array of variable;
String[] variableArray = {"id","name","address"};
Each element of the array are variable names of a class Person.
so to get the value we can use
Person::getId;
Person::getName;
Person::getAddress;
Is there any way where i can iterate the array and get the values using method reference..?
Arrays.asList(variableArray).forEach(objName -> {
Person::get 'objName'
});
You have to use reflection.
Try (If you do it in your way):
var clazz=Person.class;
Person thisReference=...;
Arrays.asList(variableArray).forEach(objName -> {
String cap = objName.substring(0, 1).toUpperCase() + objName.substring(1);
String property=null;
try{
var method=clazz.getDeclaredMethod(getDeclaredMethod);
property=(String)method.invoke(thisReference);
} catch(Exception exception){
//Do something (and maybe split up the exceptions to handle each one different)
}
//Do something with property.
});
I would do it in this way:
var clazz=Person.class;
Person thisReference=...;
//You will have to do error handling.
Method[] methodArray=new Method[]{clazz.getDeclaredMethod("getId"),
clazz.getDeclaredMethod("getName"),
clazz.getDeclaredMethod("getAddress")};
for(Method method:methodArray){
//Again, error handling...
String property = (String) method.invoke(thisReference);
}
You can certainly use reflection yourself or use a library that encapsulates the reflection part like BeanUtils. If you have a fixed set of properties only and don't aim for a generic solution using a simple mapping might work as well:
Person person = new Person(); // you need an instance of person as the getters are not static
String[] variableArray = {"id","name","address"};
Map<String, Supplier<String>> getters = new HashMap<>();
getters.put("id", person::getId);
getters.put("name", person::getName);
getters.put("address", person::getAddress);
Arrays.asList(variableArray).forEach(objName -> {
String value = getters.get(objName).get();
// do something with value
});
As the getters in Person are however not static you will need to have the actual person instance for the method references to work.
Related
I have my application.properties:
test.md5.params=something1,something4
In my java class I am getting this particular value :
and need to create same strings as present in the property file, such as
public String calculate(RequestClass request)
{
List<String> params= //I am getting the values from application.prop
**(above part id done)**
My Question is below ::
now in my params list I have [something1,something4]
so I need to concatenate both the String values like below:
String finalString=request.getSomething1()+request.getSomething4();
return finalString;
}
My Question is how to do this dynamically and in my properties file I might receive "n" of something values.
Note : I need to make the code such that my class remains constant, if in future I am adding 10 more values in properties files, my final string should be returning like
String finalString=request.getSomething1()+request.getSomething4()+....all the values.;
Through reflection this is possible, below is one implementation.
public String calculate(RequestClass request) throws InvocationTargetException, IllegalAccessException {
List<String> params = Arrays.asList("something1", "something4");
// Do your logic to get the method Names from params, below is an simple example - paramsUpdated
List<String> paramsUpdated = Arrays.asList("getSomething1", "getSomething4");
// Reflection to get the methods of request class
Method[] methods = request.getClass().getMethods();
StringBuilder sb = new StringBuilder();
for (String param : paramsUpdated) {
for (Method method : methods) {
if (param.equals(method.getName())) {
sb.append(method.invoke(request));
}
}
}
return sb.toString();
}
Let say I have 2 classes:
public class Person
{
private String name;
private int age;
private Contact contact;
//getter & setter
}
public class Contact
{
private String phone;
private String email;
//getter & setter
}
With the classes above, I want to create 2 instances of Person class, with different field value. Then I want to compare some fields of 2 objects with their getter function, but I don't want to compare all fields.
For example, I want to compare the field name and phone, then I will store this 2 getter method to a list like something below:
List<WhatShouldBeTheDataType> funcList = new ArrayList<>();
funcList.add(MyClass::getName);
funcList.add(MyClass::getContact::getPhone) //I know this won't work, what should be the solution?
then loop through the funcList, pass the 2 objects I want to compare into the function, if the value not same, write something into the database. This can be easily done with ordinary if...else... way, but is it possible to do in Java 8 way?
Below is what I want to achieve in if...else... way:
if(person1.getName() != person2.getName())
{
//message format basically is: "fieldName + value of object 1 + value of object 2"
log.append("Name is different: " + person1.getName() + ", " + person2.getName());
}
if(person1.getContact.getPhone() != person2.getContact().getPhone())
{
log.append("Phone is different: " + person1.getContact.getPhone() + ", " + person2.getContact.getPhone());
}
//other if to compare other fields
It looks like Person and MyClass refer to the same thing in your question.
You need a Function<Person,String>, since your functions accept a Person instance and return a String:
List<Function<Person,String>> funcList = new ArrayList<>();
funcList.add(Person::getName);
funcList.add(p -> p.getContact().getPhone());
For the second function, you can't use a method reference, but you can use a lambda expression instead.
Given an instance of Person, you can apply your functions as follows:
Person instance = ...;
for (Function<Person,String> func : funcList) {
String value = func.apply(instance);
}
to complete Eran's code:
boolean isEqual(Person person1, Person person2){
for (Function<Person,String> function:functionList) {
if (!function.apply(person1).equals(function.apply(person2))) return false;
}
return true;
}
then use the returned boolean to check and update your database.
Although you can use a list of functions (as suggested in Eran's answer), using comparators directly is probably more appropriate for your use case.
You can alternatively use a chain of comparators, and then use the result of compare:
Comparator<Person> comparators = Comparator.comparing((Person p) -> p.getName())
.thenComparing((Person p) -> p.getContact().getPhone());
Person p1 = null, p2 = null;
if(0 != comparators.compare(person1, person2)) {
//p1 and p2 are different
}
Even simpler (and more natural, in my opinion), is overriding equals in Person, and checking if(!person1.equals(person2))
Edit (after update of the question):
Here's a version built on a function list, dynamically generating the log content by adding a field name list:
List<Function<Person, String>> functions =
Arrays.asList(Person::getName, p -> p.getContact().getPhone());
List<String> fieldNames = Arrays.asList("Name", "Phone");
IntStream.range(0, functions.size())
.filter(i -> functions.get(i).apply(person1)
.compareTo(functions.get(i).apply(person2)) != 0)
.mapToObj(i -> String.format("%s is different: %s, %s",
fieldNames.get(i),
functions.get(i).apply(person1),
functions.get(i).apply(person2)))
.forEach(log::append);
This rather takes advantage of the fact that String is already comparable, and avoids creating comparators altogether.
I have a class like this:
public class Reza {
public Reza(Reza reza) {
}
}
I want to replace reza of input parameter of constructor to current object like this:
this = reza
How can i do?
is it possible?
No, it is not possible. this and reza represent different objects; you cannot replace the object being created in a constructor with an existing one.
However, you can copy the content of reza into this. For that, add a series of assignments like this:
public Reza(Reza reza) {
field1 = reza.field1;
field2 = reza.field2;
...
}
Impossible, but you can copy all your properties one by one (like other answer) or by reflection.
Something like this, hoping that you have getters and setters for the properties you mean to copy (I copied it from another stackoverflow answer and it looks correct):
public Reza(Reza reza) {
Method[] gettersAndSetters = reza.getClass().getMethods();
for (int i = 0; i < gettersAndSetters.length; i++) {
String methodName = gettersAndSetters[i].getName();
try{
if(methodName.startsWith("get")){
this.getClass().getMethod(methodName.replaceFirst("get", "set") , gettersAndSetters[i].getReturnType() ).invoke(this, gettersAndSetters[i].invoke(reza, null));
}else if(methodName.startsWith("is") ){
this.getClass().getMethod(methodName.replaceFirst("is", "set") , gettersAndSetters[i].getReturnType() ).invoke(this, gettersAndSetters[i].invoke(reza, null));
}
}catch (NoSuchMethodException e) {
// TODO: handle exception
}catch (IllegalArgumentException e) {
// TODO: handle exception
}
}
}
No, you cannot do that. this is, by design, a read-only constant. Assigning to it doesn't make any sense.
I am trying to get the method by java reflection, but i don't want to be specific about the parameters classes in getMethod().
public Object prepareFilter(String filter, String sort) {
Class filterClass = this.filterClass;
try {
Method createCriteriaMethod = filterClass.getMethod(CREATE_CRITERIA_METHOD);
Method orderByClauseMethod = filterClass.getMethod(ORDER_BY_CLAUSE_METHOD, String.class);
Class criteriaClass = createCriteriaMethod.getReturnType();
Object filterObject = filterClass.newInstance();
Object criteriaObject = createCriteriaMethod.invoke(filterObject);
for (ExtFilterRequest extFilter : ExtFilterRequest.decodeJson(filter)) {
StringBuilder sb = new StringBuilder()
.append(AND)
.append(WordUtils.capitalize(extFilter.getProperty()))
.append(extFilter.getCondition());
Method criteriaConditionMethod = criteriaClass.getMethod(sb.toString(), ????); // earlier extFilter.getTransformedValue().getClass()
criteriaConditionMethod.invoke(criteriaObject, extFilter.getTransformedValue());
}
String orderByClause = ExtSortRequest.getOrderByString(sort);
if (orderByClause != null)
orderByClauseMethod.invoke(filterObject, orderByClause);
return filterObject;
} catch (Exception e) {
// later
}
return null;
}
I have the methods generated by MyBatis and I want to call them by reflection with the decoded json that comes from extjs client. It looks like: operator, value and property. Following code is working with string values but i dont know what to put in the place of question marks when I call for example method which get a Date value (decoded value is always a string).
Is it possible to call getMethod with some hmm.. generic type and get the specific method? Or should I do it in another way?
Summarizing - if I have method like this:
public Criteria andSomeReferenceIsEqualTo(String value) {
addCriterion("some_ref =", value, "someRef");
return (Criteria) this;
}
and this
public Criteria andPrimDateEqualTo(Date value) {
addCriterionForJDBCDate("prim_date =", value, "primDate");
return (Criteria) this;
}
I want to call them in the same way by method I specify earlier - even if its boolean, list of values, string or integer.
I am taking in an array of methods and I want to chain them together to modify an object that I am working in.
For example I start with
"getStuff().get(1).get(3).setMoreStuff().put(stuff,6)"
I split it into an array called methods, and clean up the parameters inside each method and I try to modify this.
Object res = this;
String[] methods = targetString.split("\\.(?=\\D)");
for (String m : methods){
List<Object> params = new ArrayList<Object>();
List<Object> params = new ArrayList<Object>();
for (String p : m.split("\\(|,|\\)")) {
try {
if (p.indexOf(".") != -1){
double tempD = Double.parseDouble(p);
params.add(tempD);
} else {
int tempP = Integer.parseInt(p);
params.add(tempP);
}
} catch (Exception ex) { //not a number
params.add(p);
}
}
switch (params.size()) {
case 1:
res = res.getClass().getMethod(
params.get(0)
).invoke(res);
break;
case 2:
res = res.getClass().getMethod(
params.get(0),
params.get(1).getClass()
).invoke(res, params.get(1));
break;
case 3:
res = res.getClass().getMethod(
params.get(0),
params.get(1).getClass(),
params.get(2).getClass()
).invoke(res, params.get(1), params.get(2));
break;
}
in the end I notice that res has been modified the way that I expect. All the getters and setters are called correctly. But of course the underlying object "this" refers to has not been changed!
I guess I'm just calling the getters and setters of the copy I made in the first line!
now I can't just use
this.getClass().getMethod(...).invoke(...)
because I need to call the same getMethod on the object returned by this call.
To clarify:
Object res = this;
creates a "pointer" to this. So that when I call
res.getStuff().setStuff(foo)
this will also be modified.
but it seem that when I call
res = res.getStuff();
res = res.setStuff();
like I do in my loop,
this does not modify the underlying object this refers to?
Edit: Included more code as per request.
Edit2: added anther example, to clarify my problem.
Edit3: tried to add more code, its a bit hard to add a working program without including every class
Your general approach should be fine (although your approach to parameter conversion is somewhat ugly) - it's the specifics that are presumably causing you problems. Here's a short but complete program demonstrating calling methods and then seeing the difference afterwards:
import java.lang.reflect.*;
class Person {
private String name = "default";
public String getName() {
return name;
}
// Obviously this would normally take a parameter
public void setName() {
name = "name has been set";
}
}
class Test {
private Person person = new Person();
public Person getPerson() {
return person;
}
// Note that we're only declaring throws Exception for convenience
// here - diagnostic code only, *not* production code!
public void callMethods(String... methodNames) throws Exception {
Object res = this;
for (String methodName : methodNames) {
Method method = res.getClass().getMethod(methodName);
res = method.invoke(res);
}
}
public static void main(String[] args) throws Exception {
Test test = new Test();
test.callMethods("getPerson", "setName");
System.out.println(test.getPerson().getName());
}
}
The output is "name has been set" just as I'd expect. So see if you can simplify your code bit by bit, removing extra dependencies etc until you've got something similarly short but complete, but which doesn't work. I suspect you'll actually find the problem as you go.
Object does not change reference, its VALUE changes. So if you will call this.get("some key"), you will get value that the same value that you put using reflection.
Right?