I have a generic method which accepts class type and fields of that class to be updated. For ex:
class A {
private int a;
private int b;
}
class B {
private int c;
private int d;
}
At run time, if we pass class type as "A.class" and fieldstoBeUpdated as "b", what is the best way to access the field's getter/setter of that particular class, so that we can modify the fields.
public <T> void find(T clazz, List<String> fieldsToBeUpdated) {
List<T> collectionList = findAll((Class<T>) clazz);
collectionList.parallelStream().forEach(p -> {
if (clazz instanceof A) {
fieldsToBeUpdated.parallelStream().forEach(classFieldName -> {
switch(classFieldName) {
case "a":((A)p).setA(10);
break;
case "b":((A)p).setB(20);
break;
}
});
}
if (clazz instanceof B) {
fieldsToBeUpdated.parallelStream().forEach(classFieldName -> {
switch(classFieldName) {
case "c":((B)p).setC(30);
break;
case "d":((B)p).setD(40);
break;
}
});
}
});
}
I have written above code to achieve the same.
But the problem is that i have 30 such classes to be passed as a parameter to this generic method with list of fields of that class to update/modify.
Its not the correct implementation to write 30 such if statements checking for the class type and then type casting the object to that class.
Is there any better way to achieve the same ?
Thanks in advance.
Your A and B classes seem to both provide set/getCreatedTime and set/getUpdatedTime methods.
If the other 28 or so classes provide those as well (as implied by your question), simply have a common interface featuring those methods, for all classes to implement.
Then you can bind the generic type of your method to that interface, and forego all instanceof statements and subsequent explicit casting.
The only drawback would be if you have a field name in your List that does not pertain to the concrete class being passed to the method.
If you want to enforce this, you can use reflection on the object to discover whether the field is present by name. Then, you can easily handle any missing field this with a logged warning (or whichever mechanism you deem appropriated).
Note
As mentioned by thijs-steel, if your "time" methods share the same implementation, you can have your 30 classes extend a common abstract parent that only implements the "time" methods.
Or, you can use default methods since you're clearly using Java 8.
Example
interface I {
// assuming parameters and return types here
public void setCreatedTime(ZonedDateTime z);
public void setUpdatedTime(ZonedDateTime z);
public ZonedDateTime getCreatedTime();
public ZonedDateTime getUpdatedTime();
}
// A, B etc. all implement I
public <T extends I> void find(T object, List<String> fieldsToBeUpdated) {
fieldsToBeUpdated
.parallelStream()
.forEach(
field -> {
switch(field) {
case "a": {
try {
object.getClass().getDeclaredField("a");
// we're good
object.setCreatedTime(...);
}
catch (NoSuchFieldException e) {
// TODO something
}
break;
}
// ...
}
});
}
Update
If your classes share no common fields at all, you may want to change the whole approach altogether.
Instead of having a "one-generic-method-fits-all" logic implementation paradigm, you may want to use inheritance and have your own implementation of the find method in every single class.
That would allow for a smaller switch statement in each implementation, with error handling performed on the default case.
You could also still generalize the behavior by still having a find method taking a T extends Findable (where Findable declares the "time" methods and now the find method), and simply invoke find on the given T object.
Or even separate concerns between Findable and Timed, and have your classes implement both.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With