Search code examples
javajava-6

Finding all private fields and their corresponding getters / setters for nested classes


Situation: few apps communicate using Java DTOs. I have classes which holds as its fields another classes and they hold another another classes (up to three levels down from top DTO).

Fields could be single DTO or as (exclusively) ArrayList of other classes (DTOs). All classes are DTO. Just private fields and public setters and getters.

Now, when I get top DTO is there any way to inspect it and get all getters, including nested ones, read fields through getters and then do what I have to do (change some data, specifically remove/change some characters (I have method which does that, all final fields are eventually Strings or Integers), and then write data back using appropriate setter. I guess the best would be to find getter/setter pair per final field and do operation then move to next. Upon finding final (lowest level field) I should check if it is String (do the operation) and if Integer skip operation.

I know there is similar question but it doesn't deal with nested DTOs. Java reflection get all private fields

If possible I would avoid any 3rd party library.

Any advice on this?

UPDATE: Almost there. Here is kind of demo code, I wish it is so simple, but conceptually it is more less like that:

class SymposiaDTO

import java.util.ArrayList;

public class SymposiaDTO {
    private ProgramDTO programDTO;
    private ArrayList<PaperDTO> papersDTO;

    public ProgramDTO getProgramDTO() {
    return programDTO;
    }

    public void setProgramDTO(ProgramDTO programDTO) {
    this.programDTO = programDTO;
    }

    public ArrayList<PaperDTO> getPapersDTO() {
    return papersDTO;
    }

    public void setPapersDTO(ArrayList<PaperDTO> papersDTO) {
    this.papersDTO = papersDTO;
    }
}

class ProgramDTO

public class ProgramDTO {
    String programTitle;

    Integer programID;

    public String getProgramTitle() {
    return programTitle;
    }

    public void setProgramTitle(String programTitle) {
    this.programTitle = programTitle;
    }

    public Integer getProgramID() {
    return programID;
    }

    public void setProgramID(Integer programID) {
    this.programID = programID;
    }
}

class PaperDTO

import java.util.ArrayList;

public class PaperDTO {
    public String getTitle() {

    return title;
    }

    public void setTitle(String title) {
    this.title = title;
    }

    public ArrayList<AuthorDTO> getAuthrosDTO() {
    return authrosDTO;
    }

    public void setAuthrosDTO(ArrayList<AuthorDTO> authrosDTO) {
    this.authrosDTO = authrosDTO;
    }

    private String title;
    private ArrayList<AuthorDTO> authrosDTO;
}

class AuthorDTO

public class AuthorDTO {
    private String address;
    private String name;

    private String title;
    private String age;

    public String getAddress() {
    return address;
    }

    public void setAddress(String address) {
    this.address = address;
    }

    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

    public String getTitle() {
    return title;
    }

    public void setTitle(String title) {
    this.title = title;
    }

    public String getAge() {
    return age;
    }

    public void setAge(String age) {
    this.age = age;
    }
}

class Controller <--- by Carlos if I got his instructions right, this version gives no output at all, never even get's single iteration in for loop.

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Controller {
    @SuppressWarnings({ "unused", "rawtypes" })
    public static void main(String[] args) {

    SymposiaDTO symposiaDTO = new SymposiaDTO();
    ProgramDTO programDTO = new ProgramDTO();
    PaperDTO paperDTO = new PaperDTO();
    AuthorDTO authorDTO = new AuthorDTO();

    Class<?> topClass = symposiaDTO.getClass();
    for (Class<?> innerClass : topClass.getDeclaredClasses()) {
        for (Field field : innerClass.getDeclaredFields()) {
        if (Modifier.isPrivate(field.getModifiers())) {
            String name = Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);
            Method getter;
            try {
            getter = innerClass.getDeclaredMethod("get" + name);
            } catch (Exception ex) {
            getter = null;
            }
            Method setter;
            try {
            setter = innerClass.getDeclaredMethod("set" + name, field.getType());
            } catch (Exception ex) {
            setter = null;
            }

            // TODO real work...
            System.out.printf("%s: getter=%s, setter=%s%n", innerClass.getSimpleName(), getter, setter);
        }
        }
    }
    }
}

class Controller2 <--- slightly modified previous version, this gets into the loop, but runs twice, and it never gets deeper into nested DTOs.

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Controller2 {
    @SuppressWarnings({ "unused", "rawtypes" })
    public static void main(String[] args) {
    SymposiaDTO symposiaDTO = new SymposiaDTO();
    ProgramDTO programDTO = new ProgramDTO();
    PaperDTO paperDTO = new PaperDTO();
    AuthorDTO authorDTO = new AuthorDTO();

    Class<?> topClass = symposiaDTO.getClass();
    List<Class> classesToWalk = new ArrayList<Class>();

    for (Field field : topClass.getDeclaredFields()) {
        Class symposiaDTO2 = field.getDeclaringClass();
        classesToWalk.add(symposiaDTO2);
    }

    for (Class<?> innerClass : classesToWalk) {
        Field[] fields = Arrays.stream(innerClass.getDeclaredFields())
            .filter(field -> Modifier.isPrivate(field.getModifiers())).toArray(Field[]::new);
        for (Field field : fields) {
        String name = Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);
        Method getter;
        try {
            getter = innerClass.getDeclaredMethod("get" + name);
        } catch (Exception ex) {
            getter = null;
        }
        Method setter;
        try {
            setter = innerClass.getDeclaredMethod("set" + name, field.getType());
        } catch (Exception ex) {
            setter = null;
        }
        // TODO real work...
        System.out.printf("%s: getter=%s, setter=%s%n", innerClass.getSimpleName(), getter, setter);
        }
    }
    }
}

This is output from Controller2:

SymposiaDTO: getter=public ProgramDTO SymposiaDTO.getProgramDTO(), setter=public void SymposiaDTO.setProgramDTO(ProgramDTO)

SymposiaDTO: getter=public java.util.ArrayList SymposiaDTO.getPapersDTO(), setter=public void SymposiaDTO.setPapersDTO(java.util.ArrayList)

SymposiaDTO: getter=public ProgramDTO SymposiaDTO.getProgramDTO(), setter=public void SymposiaDTO.setProgramDTO(ProgramDTO)

SymposiaDTO: getter=public java.util.ArrayList SymposiaDTO.getPapersDTO(), setter=public void SymposiaDTO.setPapersDTO(java.util.ArrayList)


Solution

  • You could use getDeclaredClasses to find nested classes, then find the private fields and finally the getters and setters:

    Class<?> topClass = ...
    
    for (Class<?> innerClass : topClass.getDeclaredClasses()) {
        for (Field field : innerClass.getDeclaredFields()) {
            if (Modifier.isPrivate(field.getModifiers())) {
                String name = Character.toUpperCase(field.getName().charAt(0)) 
                            + field.getName().substring(1);
                Method getter;
                try {
                    getter = innerClass.getDeclaredMethod("get" + name);
                } catch (Exception ex) {
                    getter = null;
                }
                Method setter;
                try {
                    setter = innerClass.getDeclaredMethod("set" + name, field.getType());
                } catch (Exception ex) {
                    setter = null;
                }
    
                // TODO real work...
                System.out.printf("%s: getter=%s, setter=%s%n", 
                                  innerClass.getSimpleName(), getter, setter);
            }
        }
    }
    

    Edit: above code is valid for "nested classes" as mentioned in the questions title. After the sample code was added to the question it seems like the question is about getters and setters of the fields of the class:

    Use getDeclaredFields to get all fields of the class and find the corresponding getter and setter as above; use getType to get the type (class) of each field and (recursively) start over with that class.