I want to make a GUI using Java in which a user can select a bean, edit its fields, and then add an instance of the created bean to a queue. My question though is about accessing the fields. I have a class MyCompositeObject that inherits from MyParentObject. The MyParentObject is composed of multiple beans, each being composed of more beans. The class MyCompositeObject is also composed of beans. I want to find all accessible fields from MyCompositeObject.
Class MyParentObject
{
MyObjectOne fieldOne;
MyObjectTwo fieldTwo;
String name;
...
}
Class MyCompositeObject extends MyParentObject
{
MyObjectThree fieldThree;
Integer number;
...
}
Class MyObjectThree
{
boolean aBoolean;
MyObjectFour fieldFour;
...
}
I have been trying to use the BeanUtils api, but I'm getting stuck trying to get the fields of all the member beans. What I am imagining is a depth first search of all fields that could be accessed from an instance of MyCompositeObject. For example, this would include, but not be limited to, the fields: MyCompositeObject.fieldOne, MyCompositeObject.number, MyCompositeObject.fieldThree.aBoolean.
I realized when I tried:
Fields[] allFields = BeanUtils.getFields(myCompositeObject);
that I was in over my head. My research has so far not turned up any prebuilt methods that could do what I describe. Please let me know of any API methods that can do this or tell me how I can go about building my own. Thanks.
It's kind of a pain but you have to go in two dimensions
yourBeanClass.getSuperclass(); (and recursively get all superclasses until Object)
and then you can get the fields of each one
eachClass.getDeclaredFields() NOT getFields so you can get all the private fields
Once you have each field
field.getType() which returns the Class of that field
then of course, you need to go up that dudes superclass chain again to make sure you get ALL the fields of the class including the ones in the superclass
Once you have that chain of classes for that field, you can then get it's fields by repeating the above....yes, the jdk made this fun!!!! I wish to god they had a getAllDeclaredFields method so I didn't have to go up the superclass heirarchy.
IMPORTANT: you need to call field.setAccessible(true) so you can read and write to it when it is a private field by the way!!!
Here is code that gets all the fields for a Class including the superclasses..
private static List<Field> findAllFields(Class<?> metaClass) {
List<Field[]> fields = new ArrayList<Field[]>();
findFields(metaClass, fields);
List<Field> allFields = new ArrayList<Field>();
for(Field[] f : fields) {
List<Field> asList = Arrays.asList(f);
allFields.addAll(asList);
}
return allFields;
}
private static void findFields(Class metaClass2, List<Field[]> fields) {
Class next = metaClass2;
while(true) {
Field[] f = next.getDeclaredFields();
fields.add(f);
next = next.getSuperclass();
if(next.equals(Object.class))
return;
}
}
later,
Dean
Related
I have two classes (entity and DTO)
public class Deliver {
private Long id;
private String uri;
private Instant moment;
private DeliverStatus status; // enum PENDING,ACCEPTED,REJECTED
private String feedback; // feedback about received task
private Integer correctCount; // nr of correct questions
private Enrollment enrollment;
private Lesson lesson;
// constructors, getters and setters..
public class DeliverRevisionDto {
private DeliverStatus status;
private String feedback;
private Integer correctCount;
// constructors, getters and setters..
The goal is pretty simple, update the entity fields conveyed by Dto class I have the following code at Service layer (Spring Boot version 2.4.4):
#Service
public class DeliverService {
#Autowired
private DeliverRepository deliverRepository;
#Autowired
private ModelMapper modelMapper;
#Transactional
public void saveRevision(Long id, DeliverRevisionDto dto) {
Deliver deliver = deliverRepository.getOne(id);
System.out.println("BEFORE MAPPING: " + deliver.toString()); // # debug purpose
deliver = modelMapper.map(dto, Deliver.class);
// # debug purpose
TypeMap<DeliverRevisionDto, Deliver> tm = modelMapper.getTypeMap(DeliverRevisionDto.class, Deliver.class);
List<Mapping> list = tm.getMappings();
for (Mapping m : list)
{
System.out.println(m);
}
System.out.println("AFTER MAPPING: " + deliver.toString()); // # debug purpose
deliverRepository.save(deliver);
}
}
The console output is:
BEFORE MAPPING: Deliver [id=1, uri=``https://github/someone.com``, moment=2020-12-10T10:00:00Z, status=PENDING, feedback=null, correctCount=null, enrollment=com.devsuperior.dslearnbds.entities.Enrollment#7e0, lesson=com.devsuperior.dslearnbds.entities.Task#23]`
`PropertyMapping[DeliverRevisionDto.correctCount -> Deliver.correctCount]`
`PropertyMapping[DeliverRevisionDto.feedback -> Deliver.feedback]`
`PropertyMapping[DeliverRevisionDto.status -> Deliver.status]`
`AFTER MAPPING: Deliver [id=null, uri=null, moment=null, status=ACCEPTED, feedback=Muito bem cabra, tarefa aceita., correctCount=5, enrollment=null, lesson=null]
The mapping of the 3 fields in DTO is done correctly, BUT all the other fields of my entity are set to null. I know that I can skip fields according http://modelmapper.org/user-manual/property-mapping/
The problem is that I don´t want to couple the code with specific field names/getters/setters, that´s the reason I´m using ModelMapper. I wonder if there is any configuration that, upon mapping the modelmapper object says "Hey, the TARGET class have way more fields than the SOURCE class, I will left them untouched unconditionally (meaning I don´t need to say what fields are).
I'm trying to map fields between 2 classes with different set of fields (some are the same), and when I map the class with smaller set of fields to the one with bigger set of fields, the mapper set fields that don´t match with "null", I want these fields untouched (with original values) without I telling which one they are, after all, the mapper knows which ones match.
ModelMapper documentation is not the best part of that framework. Let us see what happens in your code.
Here you fetch the entity to be updated from the repo:
Deliver deliver = deliverRepository.getOne(id);
and log it having all the fields as should be. However this line:
deliver = modelMapper.map(dto, Deliver.class);
does a re-assignment to your variable deliver. This method creates a new instance of Deliver class and assigns it to variable deliver so discarding the entity fetched from repo.
This new instance will have all the fields that are not existing or not set in DTO null.
This is the API doc that my IDE provides, fotr these two different methods:
String org.modelmapper.ModelMapper.map(Object source, Class destinationType)
Maps source to an instance of destinationType. Mapping is performed according to the corresponding TypeMap. If no TypeMap exists for source.getClass() and destinationType then one is created.
Versus
void org.modelmapper.ModelMapper.map(Object source, Object destination)
Maps source to destination. Mapping is performed according to the corresponding TypeMap. If no TypeMap exists for source.getClass() and destination.getClass() then one is created.
It might not be clearly stated that the first method actually creates a new instance based on the type (Class) passed but it should be clear that ModelMapper cannot alter some arbitrary variable just by knowing the type. You need to pass the variable to alter as method parameter.
I am trying to create a helper method that will take in the names (type String) of the member variables (could be any number of member variables) and automatically initialize/create the member variables as well as the getter methods. So I would call something like:
helperClass("hello", "myName", "is", "bob")
and the helperClass would look something like this:
public class helperClass {
helperClass(String ...a) {
for (String s: a)
//create member variables and getter methods dynamically
}
So, in the end, the caller of the function would have something like this:
public class helperClass {
private String hello
private String myName
private String is
private String bob
//getter methods below
...
}
Coming from Python so wasn't sure if this type of stuff is doable in Java.
Yes. You can create the getter and setter methods dynamically.
Tutorial for Java Dynamic POJO creation . But this method will involve you creating a predefined string which contains the method declaration.
Eg :
String s= "public void doSonething(String ... args){ // Function Body }" .
You can then convert this string into a function at runtime. Based on your need, you can define a custom string that contains the method declaration which you need. See some examples in the above tutorial link I have attached.
i think the answer is to use an IDE. They all have "add property" functions which will generate the declaration and appropriately named getters and setters.
Should you prefer to manually enter your properties, They all also have generate getter/setter functions which will look at the properties you have entered (work out which getters and setters are missing) and offer to create approporiately named getters and setters in bulk for the ones you've selected.
To answer your specific question, yes you can write your own class that takes a list of strings (i.e. property names) and print them out as a series of getters and setters, this is basic string concatenation:
private String generateGetter(String propName) {
return String.format(" public String get%s()\n return this.%s;\n }", StringUtils.capitalize(propName), propName);
}
To convert the first letter of the propName to upper case (the convention for getter and setter methods, you can do it yourself or use apache's string utils.
I want to refactor my code, because it contains redundant class-declarations.
I have a class of type ContainerClass1. Let’s assume the class-type contains only an field for an int-variable named „content“.
Another class of type ContainerFiller1 contains a method, that takes an instance of ContainerClass1 as a parameter and sets the field to a certain value. ContainerFiller1 looks like this:
public class ContainerFiller1 {
public void fill( ContainerClass object_A ) {
object_A.content = 99;
}
}
So I‘m using the dot-notation to declare, where to find the field (in object_A in this case).
I have eight other objects as instances of class ContainerClass (ContainerClass object_B, ContainerClass object_C, ContainerClass object_D, etc.).
And here‘s my problem: object_A.content = 99 is hard-wired, so within the dot-notation I can not take the object-name as a variable to adjust my path to the field. As a workaround I have declared nine separate ContainerFiller-Classes ( ContainerFiller1, …, ContainerFiller9) with each one having a different field-setter. As an example ContainerFiller2 contains a hard-wired setter object_B.content = 99 .
I want to get rid of my redundant classes and make a universal class of type ContainerFiller out of them. Is there a way to change the field-setter-notion in a way, I can reference the object-name in the parameter-list of fill(ContainerClass anyObject) and adjust the field-setter dynamically? Suggestions for alternatives for the dot-notation would be welcome as well.
Thanks in advance
Threx
You could use a parameter in the constructor:
public class ContainerFiller {
private amount;
public ContainerFiller(int amount) {
this.amount = amount
}
public void fill( ContainerClass container ) {
container.content = this.amount;
}
}
And then use it like this:
ContinerFiller fillerA = new ContainerFiller(99)
fillerA.fill(someContainer)
I'm trying to create a generic DAO in order to avoid having more or less the same code in many separate DAOs.
My problem is that in the following lines of code:
private BaseDAOImpl<Artist> baseDAOArtist = new BaseDAOImpl<>(Artist.class);
private BaseDAOImpl<ArtistRelation> baseDAOArtistRelation = new BaseDAOImpl<>(ArtistRelation.class);
The first one seems to be skipped.
An excerpt of the BaseDAOImpl:
public class BaseDAOImpl<T> implements BaseDAO<T> {
private Class<T> entity;
private DAOFactory daoFactory = Config.getInstance().getDAOFactory();
private static String SQL_FIND_BY_ID;
public BaseDAOImpl(Class entity) {
this.entity = entity;
SQL_FIND_BY_ID = "SELECT * FROM VIEW_" + entity.getSimpleName() + " WHERE id = ?";
}
}
Is it not possible to instantiate multiple objects this way?
Yes. It's not clear what you mean by "The first one seems to be skipped." but it could be that your using a static value for "SQL_FIND_BY_ID"? As at the moment:
private BaseDAOImpl<Artist> baseDAOArtist = new BaseDAOImpl<>(Artist.class);
Creates two instance variables and sets the value of SQL_FIND_BY_ID then:
private BaseDAOImpl<ArtistRelation> baseDAOArtistRelation = new BaseDAOImpl<>(ArtistRelation.class);
Creates two new instance variables and will change the value "SQL_FIND_BY_ID" for both instances.
Without a more detailed description of the error I am more or less guessing now, but judging from variable names and the code snippet I would suspect the static field SQL_FIND_BY_ID to be the cause.
When you instantiate the two DAOs, the second execution of the constructor BaseDAOImpl will overwrite the value of the static field. If the DAO relies on the SQL query stored there, it will always query for the entity of the last instantiated DAO.
Static fields and methods are shared among all instances of a class even if they differ on their generic parameters. In contrast to e.g. C++'s templates, there are no separate classes generated for each generic parameter.
To achieve the desired behavior of separate queries for each entity you may change the static field to a non-static member.
I have an abstract class as follows. I want to get all the values of member variables.
public abstract class PARAMS {
public static final String NAME1 = "VAL1";
public static final String NAME2 = "VAL2";
public static final String NAME3 = "VAL3";
}
The values are retrieved using reflection as follows.
Field[] fields = PARAMS.class.getFields();
for (Field field : fields) {
String name = field.getName() ;
String value = (String) field.get(name);
}
This is the first time I am experimenting with reflection. Is this a correct way to achieve the goal? I would like to know what are the pitfalls in using reflection in this case.
You code iterates over both static and private fields. So you should check that you iterate over static fields only.
for (Field field : PARAMS.class.getFields()) {
if (Modifiered.isStatic(field.getModifiers())) continue;
String name = field.getName() ;
String value = (String) field.get(PARAMS.class);
}
NB: as Jon mentioned, for static field access the instance parameter is ignored. However, I prefer passing in the class instead of null since this is a better documentation of the indent.
It is however even better practice to annotate your fields with an annotation so that you only get those fields that you really want no other static fields added by other programmers (or even the Java language behind the scenes). If you do so, your code would look like
for (Field field : PARAMS.class.getFields()) {
if (!field.isAnnotationsPresent(YourAnnotation.class)) continue;
String name = field.getName() ;
String value = (String) field.get(PARAMS.class);
}
It's not quite correct - the argument to get should ideally be null for the sake of readability: the point of that argument is to give it a target for when you're retrieving instance fields.
So your code can be just:
Field[] fields = PARAMS.class.getFields();
for (Field field : fields) {
String name = field.getName() ;
String value = (String) field.get(null);
}
Now, this should work... but what are you going to do with these values? Is there any reason why you want to do this rather than creating an immutable Map<String, String> which is exposed directly?
Reflection is fine where it's necessary, but you haven't given enough information to determine whether it's actually necessary in this case.
another problem, getFields return all accessible fields (static or not) of this class and all its superclasses. Not a problem for the specific code you posted, since the only superclass is Object which has no public field.
I would at least test if the field is declared in the correct class - getDeclaringClass() - and if it has the correct return type - getType().
Using an Annotation, as Adrian suggested, is best IMHO.