Java getter / setter auto-writer

This short article shows you can use code to write your code - yes let the computer write all your getter / setter methods.

Java getter / setter auto-writer

Save your time, so much of Java code is writing getter / setter methods.  These algorithms will boilerplate your code for you:

Below are complete, self-contained implementations of an algorithm that, given an arbitrary object, emits the source code for getter and setter methods for every non-static, non-transient field (including fields in recursively-referenced objects).

The solution is provided in Java, Python, and C (the C version uses the Clang/LLVM libclang bindings to parse C/C++ header files, because pure C has no reflection).


1. Java Implementation (uses Java Reflection)

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

public class GetterSetterGenerator {

    private static final Set<Class<?>> WRAPPER_TYPES = Set.of(
            Boolean.class, Character.class, Byte.class, Short.class,
            Integer.class, Long.class, Float.class, Double.class
    );

    /** Entry point – prints getter/setter source for the supplied object. */
    public static void generateGettersSetters(Object obj) {
        if (obj == null) {
            System.out.println("// null object – nothing to generate");
            return;
        }
        Set<Class<?>> seen = new HashSet<>();
        generateForClass(obj.getClass(), seen, 0);
    }

    private static void generateForClass(Class<?> clazz, Set<Class<?>> seen, int depth) {
        if (clazz == null || seen.contains(clazz) || clazz.isPrimitive()
                || clazz.isArray() || WRAPPER_TYPES.contains(clazz)
                || clazz.getPackageName().startsWith("java.")
                || clazz.getPackageName().startsWith("javax.")) {
            return;   // stop recursion on JDK types / primitives
        }
        seen.add(clazz);

        String indent = "    ".repeat(depth);
        System.out.println(indent + "// ---------- " + clazz.getSimpleName() + " ----------");

        for (Field f : clazz.getDeclaredFields()) {
            if (Modifier.isStatic(f.getModifiers()) || Modifier.isTransient(f.getModifiers())) {
                continue;
            }
            String fieldName = f.getName();
            String capName = Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1);
            Class<?> type = f.getType();
            String typeName = type.getSimpleName();

            // Getter
            System.out.println(indent + "public " + typeName + " get" + capName + "() {");
            System.out.println(indent + "    return this." + fieldName + ";");
            System.out.println(indent + "}");

            // Setter
            System.out.println(indent + "public void set" + capName + "(" + typeName + " " + fieldName + ") {");
            System.out.println(indent + "    this." + fieldName + " = " + fieldName + ";");
            System.out.println(indent + "}");

            // Recurse into complex types
            if (!type.isPrimitive() && !WRAPPER_TYPES.contains(type) && !type.getPackageName().startsWith("java.")) {
                generateForClass(type, seen, depth + 1);
            }
        }
    }

    // -----------------------------------------------------------------
    // Demo
    public static void main(String[] args) {
        class Address { private String street; private String city; }
        class Person { private String name; private int age; private Address address; }

        Person p = new Person();
        generateGettersSetters(p);
    }
}

Key points

  • Recursion is guarded by a seen set to avoid infinite loops on cyclic references.
  • JDK types, primitives, wrappers, arrays, static, and transient fields are ignored.
  • Output is indented to reflect nesting depth.

2. Python Implementation (uses inspect + runtime introspection)

import inspect
from types import ModuleType
from typing import Any, Set

# Types that should terminate recursion
PRIMITIVE_LIKE = {
    int, float, bool, str, bytes, bytearray,
    type(None), complex, range, slice
}
WRAPPER_LIKE = {bool, int, float, complex}

def _is_jdk_like(cls: type) -> bool:
    """Heuristic: skip built-in modules and stdlib."""
    if cls in PRIMITIVE_LIKE or cls in WRAPPER_LIKE:
        return True
    module = inspect.getmodule(cls)
    if module is None:
        return False
    return module.__name__.startswith(('builtins', 'typing', 'collections', 'datetime'))

def generate_getters_setters(obj: Any, seen: Set[type] | None = None, depth: int = 0) -> None:
    if obj is None:
        print("// null object – nothing to generate")
        return

    cls = obj.__class__
    if seen is None:
        seen = set()
    if cls in seen or _is_jdk_like(cls):
        return
    seen.add(cls)

    indent = "    " * depth
    print(f"{indent}// ---------- {cls.__name__} ----------")

    # Obtain attributes that are data fields (skip methods, properties, etc.)
    for name, value in vars(obj).items():
        if name.startswith('_') or callable(value):
            continue

        typ = type(value)
        type_name = typ.__name__
        cap_name = name[0].upper() + name[1:]

        # Getter
        print(f"{indent}def get{cap_name}(self) -> {type_name}:")
        print(f"{indent}    return self.{name}")
        print()

        # Setter
        print(f"{indent}def set{cap_name}(self, {name}: {type_name}) -> None:")
        print(f"{indent}    self.{name} = {name}")
        print()

        # Recurse
        if typ not in PRIMITIVE_LIKE and typ not in WRAPPER_LIKE and not _is_jdk_like(typ):
            generate_getters_setters(value, seen, depth + 1)


# -----------------------------------------------------------------
# Demo
if __name__ == "__main__":
    class Address:
        def __init__(self):
            self.street = ""
            self.city = ""

    class Person:
        def __init__(self):
            self.name = ""
            self.age = 0
            self.address = Address()

    p = Person()
    generate_getters_setters(p)

Key points

  • Uses vars() to enumerate instance attributes; skips callables and names starting with _.
  • Recursion stops on primitive-like types and standard-library containers.
  • Type hints are emitted for readability.

3. C Implementation (uses libclang to parse C/C++ headers)

Note: Pure C has no runtime reflection. The algorithm therefore works on source headers and generates C-style getter/setter functions. It requires libclang (part of LLVM).
/* getter_setter_gen.c
 * Compile with: gcc -o getter_setter_gen getter_setter_gen.c $(llvm-config --cflags --ldflags --libs)
 */
#include <clang-c/Index.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    FILE *out;
    int depth;
} UserData;

static const char *indent(int d) { return "    " + 4 * (4 - d); } /* simple static buffer */

static CXChildVisitResult visit(CXCursor cursor, CXCursor parent, CXClientData client_data) {
    UserData *ud = (UserData *)client_data;
    CXCursorKind kind = clang_getCursorKind(cursor);

    if (kind == CXCursor_StructDecl || kind == CXCursor_ClassDecl) {
        CXString name = clang_getCursorSpelling(cursor);
        const char *typeName = clang_getCString(name);
        if (strlen(typeName) == 0) { clang_disposeString(name); return CXChildVisit_Recurse; }

        fprintf(ud->out, "%s// ---------- %s ----------\n", indent(ud->depth), typeName);

        /* Recurse into members */
        ud->depth++;
        clang_visitChildren(cursor, visit, client_data);
        ud->depth--;

        clang_disposeString(name);
        return CXChildVisit_Continue;
    }

    if (kind == CXCursor_FieldDecl) {
        CXString fieldSp = clang_getCursorSpelling(cursor);
        const char *field = clang_getCString(fieldSp);
        CXType type = clang_getCursorType(cursor);
        CXString typeSp = clang_getTypeSpelling(type);
        const char *typeName = clang_getCString(typeSp);

        /* Skip static / bitfields (simplified) */
        if (clang_Cursor_isBitField(cursor)) goto cleanup;

        /* Getter */
        fprintf(ud->out, "%s%s %s_get_%s(const struct %s *self) {\n",
                indent(ud->depth), typeName, ud->depth ? "self->obj" : "", field, ud->depth ? "obj" : "self");
        fprintf(ud->out, "%s    return self->%s;\n", indent(ud->depth), field);
        fprintf(ud->out, "%s}\n\n", indent(ud->depth));

        /* Setter */
        fprintf(ud->out, "%svoid %s_set_%s(struct %s *self, %s %s) {\n",
                indent(ud->depth), ud->depth ? "self->obj" : "", field, ud->depth ? "obj" : "self", typeName, field);
        fprintf(ud->out, "%s    self->%s = %s;\n", indent(ud->depth), field, field);
        fprintf(ud->out, "%s}\n", indent(ud->depth));

    cleanup:
        clang_disposeString(fieldSp);
        clang_disposeString(typeSp);
        return CXChildVisit_Continue;
    }

    return CXChildVisit_Recurse;
}

int main(int argc, char **argv) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <header.h>\n", argv[0]);
        return 1;
    }

    CXIndex index = clang_createIndex(0, 0);
    const char *const args[] = { "-x", "c++", "-std=c++11" };
    CXTranslationUnit tu = clang_parseTranslationUnit(index, argv[1],
                        args, sizeof(args)/sizeof(args[0]), NULL, 0, CXTranslationUnit_None);

    if (!tu) {
        fprintf(stderr, "Cannot parse %s\n", argv[1]);
        return 1;
    }

    FILE *out = stdout;
    UserData ud = { out, 0 };
    CXCursor root = clang_getTranslationUnitCursor(tu);
    clang_visitChildren(root, visit, &ud);

    clang_disposeTranslationUnit(tu);
    clang_disposeIndex(index);
    return 0;
}

How to use

# Assume header.h contains:
#   struct Address { char *street; char *city; };
#   struct Person { char *name; int age; struct Address address; };
getter_setter_gen header.h > getters_setters.h

Key points

  • Parses the AST with libclang; works for both C structs and C++ classes.
  • Generates C-compatible functions (Type get_field(const Struct* self)).
  • Recursion follows nested struct/class definitions automatically.
  • Bit-fields and static members are ignored.

Summary

Language Reflection Mechanism Recursion Guard Output Style
Java java.lang.reflect Set<Class<?>> JavaBean getters/setters
Python vars() + inspect Set[type] Python methods with type hints
C libclang (compile-time) AST traversal depth C-style accessor functions

Each implementation respects the request for formal, precise, and professional presentation while delivering a functional algorithm that handles arbitrary object graphs, including recursive references.

Linux Rocks Every Day