I am having a domain object with over 90 attributes. In all these attributes there are ., which I want to replace with an empty string.
I could do now:
for (int i = 0; i < list.size(); i++) {
list.get(i).getProp1().replace(".", "");
list.get(i).getProp2().replace(".", "");
//yadadada ...
list.get(i).getProp90().replace(".", "");
}
However, that is extremely boring and takes a lot of code to write. Is there a way to do this much more elegantly and faster?
I appreciate your ideas
What about making a method updateProp (since that's what you need) in your Domain class like this:
public void updateProp(int index) {
prop[index] = prop[index].replace(".", "");
}
and then calling it in a loop
for (int i = 0; i < list.size(); i++) {
for(int j=0; j < prop.length; j++) { /*use a getter, instance or class
reference for obtaining prop.length*/
list.get(i).updateProp(j);
}
}
Storing the Prop in its own Object could also do the trick, but I guess it'll break more code than it will fix.
You have to use java reflection:
Method[] methods = DomainObject.class.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().startsWith("getProp")) {
String result = (String) method.invoke(object, new Object[0]);
result = result.replace(".", "");
Method setter = DomainObject.class.getMethod(method.getName()
.replace("get", "set"), String.class);
setter.invoke(object, result);
}
}
Also don't forget String is immutable, the replace method doesn't change it, the call simply returns the changed version.
You're suggesting that you have a Java class which has up to 90 properties with a consistently incorrectly formatted data field. This question is possibly an X-Y Problem
There is a reason the attributes are stored with a ., and a reason you need them without the .
By understanding these reasons better, and their context, a cleaner solution will present itself.
For example, is the removal of the . purely for display purposes? Perhaps consider a generic function at the point of output to strip the .'s
Or maybe it's some kind of behaviour side-effect during data capture, in which case this needs to be part of an input filter.
Or is this a one-off ETL routine? In which case perhaps it could be done on the data columns direct by some generic SQL or grep / sed script.
If you cannot modify your object (maybe is auto-generated or something) you can achieve your goal through reflection:
for(MyObject a: list){
Field[] fields = MyObject.class.getDeclaredFields();
for (Field f : fields) {
try {
if (f.get(a) instanceof String) {
String str = ((String) f.get(a)).replace(".", "");
f.set(a, str);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
If you don't want to modify the values of your objects you can create The Monster:
List<Map<String, String>> monster = new ArrayList<Map<String, String>>();
for(MyObject a: list){
Field[] fields = MyObject.class.getDeclaredFields();
Map<String, String> map = new HashMap<String, String>();
for (Field f : fields) {
try {
if (f.get(a) instanceof String) {
String str = ((String) f.get(a)).replace(".", "");
map.put(f.getName(), str);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
monster.add(map);
}
Now you have a beatiful list of maps for fields created through reflection. You can access to prop1 of first object via
String obj1prop1 = monster.get(0).get("prop1"); //Value of prop1 for first object in list.
I guess the best way for me would be to create a method in your Object (the one from your List) and in this method, you directly change all the properties as you did here. Than you call your method instead of all of those.
The treatment will be directly on your Object ;)
for (int i = 0; i < list.size(); i++) {
list.set(i,list.get(i).update());
}
in your Object :
public void update()
{
prop1 = prop1.replace(".","");
etc for all the properties
}
Or you could use something like : Field[] getFields() if your attribute are public.
But actually, I think you should do this replace when you are creating your object, so before you already add it in the list. It would be better.
Related
I hava a POJO class which has a lot of getters. Now I’m using the class Method from the java.lang.reflect package to get all the get methods from that class. I want to invoke those getters but I don’t have idea how. arraylistFilter is an Arraylist<pojo> that contains the result from my request. Option is an Object Field data type which is the one that actually has like 100 getters. How can I get each of them without needed to call 1 by 1. what goes in my .??? there is where I want to be able to invoke my getters.
try {
Class<? extends Options> testObject = new Options().getClass();
Method[] methods = testObject.getMethods();
for (Method method : methods) {
String name = method.getName();
if (method.getName().startsWith("get") &&
method.getGenericParameterTypes().length == 0) {
for (int i = 0; i < arrayListFilter.size(); i++) {
arrayListFilter.get(i).getOptions().???;
}
}
}
} catch (Exception e) {
// do something with the exceptions
}
It will be something like this:
try {
Method[] methods = Options.class.getMethods();
for (Method method : methods) {
String name = method.getName();
if (method.getName().startsWith("get") &&
method.getParameterTypes().length == 0) {
for (int i = 0; i < arrayListFilter.size(); i++) {
Object value = method.invoke(arrayListFilter.get(i).getOptions());
// The actual type of 'value' will depend on the getter's
// formal return type AND the actual type it returns.
// When the return type is a primitive, it is mapped to
// the corresponding wrapper.
}
}
}
} catch (Exception e) {
// do something with the exceptions
}
We have simplified / fixed the code that gets the Method objects, and have changed getGenericParameterTypes() to getParameterTypes(). Generic type parameters are not relevant here, but you do need to filter out getters that require arguments, because there is no way to supply sensible argument values.
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 working with some code where the user sends in an object which can have upwards of 100 values to it. (Let's use a car for example).
updateDatabasePrep(carObject);
The car object COULD have things like name, milage, vin, color, etc... but say I only set the color and then pass the object.
In my method:
public void updateDatabasePrep(Car carObject){
NewObject myObject = newObject(); //Initialized, but empty
String x1 = carObject.getMilage();
String x2 = carObject.getColor();
String x3 = carObject.getName();
//These will hold null values no problem
//Once the rest of the data has been collected:
//These, however, will error out when I try to set the null values.
myObject.setMilage(x1);
myObject.setColor(x2);
myObject.setName(x3);
}
It uses accessor methods which will pull the data from the passed object and then tries to set said values. In the above situation, it will throw a null pointer error as the milage and name are null; only the color is not.
My goal is to update the database with ONLY the values that have been set. In this case, I COULD write a ton of if/ else or try/ catch statements for every single one, that would work, but I would prefer not to.
Is there a shorter way to do this in Java? Is there a method where you can ONLY set data if it is not-null other than if/ else, try/catch, and going through every single setter method and telling it to skip null values?
EDIT:
Just to clarify, the Nullpointerexception will get thrown on when it tries to set a null value. IE
myObject.setMilage(x1);
since people we asking.
Assuming myObject is always going to be the same type, you could use reflection to do a mapping phase that puts the getter and setter methods together in a hashmap like so:
private HashMap<Method, Method> methodMap;
private void doReflection()
{
Method[] carMethods = carObject.getClass().getDeclaredMethods();
ArrayList<Method> getters = new ArrayList<>();
for(int i = 0; i < carMethods.size(); i++)
if(carMethods[i].getName().startsWith("get")) getters.add(carMethods[i]);
methodMap = new HashMap<>();
for(Method m : getters)
{
String fieldName = m.getName().substring(3);
Method setter = myObject.class.getMethod("set" + fieldName, m.getParameterTypes());
if(setter != null) methodMap.put(m, setter);
}
}
Now you can iterate the HashMap to do assignments like so:
for(MapEntry<Method, Method> entry : methodMap.entrySet())
{
Method getter = entry.getKey();
Method setter = entry.getValue();
Object o = getter.invoke(carObject, null);
if(o != null) setter.invoke(myObject, getter.invoke(carObject, null));
}
Reflection is your answer. You may also try using some libraries making it a little more convenient (e.g. commons-beanutils).
So you may do something like:
private void copyIfSpecified(final Car from, final NewObject to, final String propName)
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
final Object value = PropertyUtils.getSimpleProperty(from, propName);
if (value != null) {
PropertyUtils.setSimpleProperty(to, propName, value);
}
}
and call it from your method:
public void updateDatabasePrep(Car carObject){
NewObject myObject = new NewObject(); //Initialized, but empty
try {
copyIfSpecified(carObject, myObject, "milage");
copyIfSpecified(carObject, myObject, "color");
copyIfSpecified(carObject, myObject, "name");
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
throw new RuntimeException("Error while populating fields", ex);
}
}
This way you explicitly specify the properties to copy but avoid excessive if statements. In case you don't have to make sure all the fields you want are always copied (e.g. the fieldset is fixed and don't tend to be changed over time) you can do the whole copy thing via reflection (i.e. get all fields of the source object and copy non-null values to the destination object's fields - see PropertyUtils.copyProperties(..) implementation).
ps: note that using this method applies additional restrictions to your classes (Car and NewObject) - they should be java beans: see What is a JavaBean exactly?
pps: and also note that using reflection takes A LOT more time than plain if-statements boilerplate - if you need performance you should think twice.
I am not sure how you are updating DB, using procedure or prepared call. However, below might be helpful to you.
You can get list of fields having null value -
public List<String> getNullFields(Car car) {
Field[] fields = car.getClass().getDeclaredFields();
List<String> result = new ArrayList<String>();
for (Field field : fields) {
try {
field.setAccessible(true);
Object value = field.get(car);
if(value == null) {
result.add(field.getName());
}
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
return result;
}
Or else, you can get fields having not null value as well (exact values as well) using similar approach.
Then, you can construct your prepared statement dynamically based on this result. Good luck!
working & optimized code for only getters & setters (including "is" for boolean types)
public void copyCabInfo(CabInfo pCabInfo){
for(Map.Entry<Method, Method> entry : doReflection().entrySet())
{
Method getter = entry.getKey();
Method setter = entry.getValue();
Object o=null;
try {
o = getter.invoke(pCabInfo, null);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e1) {
e1.printStackTrace();
}
if(o != null)
try {
setter.invoke(this, getter.invoke(pCabInfo, null));
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
private static HashMap<Method, Method> methodMap;
private static HashMap<Method, Method> doReflection()
{
if(methodMap!=null && !methodMap.isEmpty())
return methodMap;
Method[] carMethods = CabInfo.class.getDeclaredMethods();
ArrayList<Method> getters = new ArrayList<>();
for(int i = 0; i < carMethods.length; i++)
if(carMethods[i].getName().startsWith("get") || carMethods[i].getName().startsWith("is") ) getters.add(carMethods[i]);
methodMap = new HashMap<>();
for(Method m : getters)
{
String methodName=m.getName();
String fieldName = methodName.startsWith("is")?methodName.substring(2):methodName.substring(3);
Method setter=null;
try {
setter = CabInfo.class.getMethod("set" + fieldName, m.getReturnType());
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(setter != null) methodMap.put(m, setter);
}
return methodMap;
}
I have a program in java that I wrote to return a table of values. Later on as the functions of this program grew I found that I would like to access a variable within the method that isn't returned but I am not sure the best way to go about it. I know that you cannot return more than one value but how would I go about accessing this variable without a major overhaul?
here is a simplified version of my code:
public class Reader {
public String[][] fluidigmReader(String cllmp) throws IOException {
//read in a file
while ((inpt = br.readLine()) != null) {
if (!inpt.equals("Calls")) {
continue;
}
break;
}
br.readLine();
inpt = br.readLine();
//set up parse parse parameters and parse
prse = inpt.split(dlmcma, -1);
while ((inpt = br.readLine()) != null) {
buffed.add(inpt);
}
int lncnt = 0;
String tbl[][] = new String[buffed.size()][rssnps.size()];
for (int s = 0; s < buffed.size(); s++) {
prse = buffed.get(s).split(dlmcma);
//turns out I want this smpls ArrayList elsewhere
smpls.add(prse[1]);
//making the table to search through
for (int m = 0; m < prse.length; m++) {
tbl[lncnt][m] = prse[m];
}
lncnt++;
}
//but I return just the tbl here
return tbl;
}
Can anyone recommend a way to use smpls in another class without returning it? Is this perhaps when you use a get/set sort of setup?
Sorry if this seems like an obvious question, I am still new to the world of modular programming
Right now you have this tbl variable. Wrap it in a class and add the list to the class.
class TableWrapper {
// default accessing for illustrative purposes -
// setters and getters are a good idea
String[][] table;
List<String> samples;
TableWrapper(String[][] table, List<String> samples) {
this.table = table;
this.samples = samples;
}
}
Then refactor your method to return the wrapper object.
public TableWrapper fluidigmReader(String cllmp) throws IOException {
// your code here
String tbl[][] = new String[buffed.size()][rssnps.size()];
TableWrapper tw = new TableWrapper(tbl,smpls);
// more of your code
return tw;
}
Then later in your code where you were going
String[][] tbl = fluidigmReader(cllmp);
You instead go
TableWrapper tw = fluidigmReader(cllmp);
String[][] tbl = tw.table;
List<String> smpls = tw.samples;
If you had used a dedicated class for the return value (such as the TableWrapper mentioned in another answer), then you could add additional fields there.
That is the good thing about classes - they can be extended. But you cannot extend String[][] in Java.
You can set a field, instead of a local variable, which you can retrieve later with a getter. You want to avoid it unless it is needed, but in this case it is.
You can use class(Inside Reader class) variable for this. But make sure that it's read/write is synchronized
I have four different classes classA, classB, classC and classD. All the four classes have the same static method search() which takes two string parameters. If i want to invoke static method search in four different classes from main class at once. How can I do that. For now my code is as follows for main class. I need to execute the same thing for other 3 classes also. How can i do that and display the results of other 3 in the same way as for classA. The way search is done in 4 classes r different but they should give the same result.
Main() {
Object[] zy;
for (String pattern : Read.arrayList) {
List<Integer> results = ClassA.findAll(pattern, dataToSearch);
zy = results.toArray();
for (int i = 0; i < zy.length; i++) {
System.out.println(" Pattern searched " + pattern + " match is found at index : "+ results);
}
}
if (zy.length == 0) {
System.out.println("Nothing matched");
}
}
I strongly recommend you change this to non-static methods. Look how easy and nice is when you will seperate an interface:
public interface Common {
List<Integer> findAll(String pattern, String dataToSearch);
}
public class A implements Common ...
public class B implements Common ...
public class C implements Common ...
public class D implements Common ...
// in main:
List<Common> allYourClasses = new ArrayList<Common>();
allYourClasses.add(new A());
allYourClasses.add(new B());
allYourClasses.add(new C());
allYourClasses.add(new D());
List<Integer> result = new ArrayList<Integer>();
for (Common c : allYourClasses) {
result.addAll(c.findAll(pattern, dataToSearch));
}
1 - You should NOT do this. Avoid static methods. One of the reason being they can not be called without the exact class. A group of classes that implement a simple interfaces will work faster, safer and better in every way
2 - You can (but you shouldn't) do something like this:
for (Class<?> clazz : new Class[] { ClassA.class, ClassB.class,
ClassC.class }) {
Object[] zy = null;
String dataToSearch = "";
String[] arrayList = { "a" };
for (String pattern : arrayList) {
List<Integer> results = findAllForClass(clazz, pattern,
dataToSearch);
zy = results.toArray();
for (int i = 0; i < zy.length; i++) {
System.out.println(" Pattern searched " + pattern
+ " match is found at index : " + results);
}
}
if (zy.length == 0) {
System.out.println("Nothing matched");
}
}
#SuppressWarnings("unchecked")
public static List<Integer> findAllForClass(Class<?> clazz, String pattern,
String dataToSearch) {
List<Integer> list = null;
try {
list = (List<Integer>) clazz.getDeclaredMethod("findAll", String.class,
String.class).invoke(null, pattern, dataToSearch);
} catch (Exception e) {
list = Collections.emptyList();
}
return list;
}
You see the #supresswarning and the try/catch? well, this is a hint: is telling you you this code is at least suspicious. It is in fact unsafe, non well performant, and is a stupid workaround.
(But we all did something like that once in our lives)
I can't really figure out why would anyone do that.
That said, you could have a method taking a Class as a parameter and calling the method explicitly by name (getMethod.../invoke()).
That puts you back in non static world and you can iterate over the classes you want to invoke. (But again, why use statics in the first place?)
Pseudo untested code:
public void invokeStatic(Class clazz, String method, Class<?> paramsTypes[], Object[] params) {
Method method = clazz.getMethod(method, paramsType);
method.invoke(params);
}
If you want to group all of the results together, just keep adding results to your list:
List<Integer> results = ClassA.findAll(pattern, dataToSearch);
results.addAll(ClassB.findAll(pattern, dataToSearch));
// etc.