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.
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
seenset 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.