About#
OBJ2GUI3 is the successor of OBJ2GUI2 : a Java Swing library for automatic GUI generation from annotated objects, redesigned from scratch with a more modern approach and built-in memory leak prevention.
Select one object and you get a classic form. Select several and you get multi-object editing : fields with differing values are highlighted in orange, and typing a value applies it to the whole selection.

What changes from OBJ2GUI2#
| OBJ2GUI2 | OBJ2GUI3 | |
|---|---|---|
| Annotations | on fields | on getters — computed/derived values can be displayed too |
| Property names | strings ("name") | type-safe method references (Person::getName) |
| Listeners | strong references | weak references — discarded forms cannot leak memory |
| Undo/Redo | none | built-in, including multi-object edits as compound commands |
| Mixed selection | single type | heterogeneous — objects grouped by type automatically |
| Look & Feel | Swing default | FlatLaf light/dark + MigLayout |
How it works#
Your DTO implements Observable and annotates its getters — which means a form can also display computed values, something field annotations could never do:
public class Person implements Observable {
public final ObservableSupport observable = new ObservableSupport(this);
@O2G3Property(label = "Name", orderDisplay = 10)
public String getName() { return name; }
public void setName(String name) {
String old = this.name;
this.name = name;
firePropertyChange(old, name); // setter name determined automatically
}
// Read-only computed property
@O2G3Property(label = "Summary", orderDisplay = 30, readOnly = true)
public String getSummary() { return name + " (" + age + ")"; }
// Nested object gets its own sub-form
@O2G3NestedObject
public Address getAddress() { return address; }
// List property with its own add/remove editor
@O2G3ListProperty(label = "Dogs", orderDisplay = 100, itemType = Dog.class)
public List<Dog> getAnimaux() { return animaux; }
}Then a fluent builder assembles the form — with undo/redo and ComboBox bindings fed from your domain objects:
UndoRedoManager undoManager = new UndoRedoManager(50);
Binding deptBinding = new Binding(departments, Person.class,
PropertyNameExtractor.getPropertyName(Person::getDepartment));
JPanel form = new MultiObjectFormBuilder(undoManager)
.withObjects(selectedPersons) // one or many
.withBindings(List.of(deptBinding))
.build();Widgets are auto-detected from the property type (TextField, Spinner, Slider, CheckBox, ComboBox, TextArea…), and programmatic changes to the model are reflected back into the form thanks to the observable DTOs — through weak listeners, so a closed form can never keep your objects alive.
Screenshots#


Roadmap#
- Externalized labels (i18n) with convention-over-configuration and fallback to annotation labels
- Plugin system for custom types
- Dark-mode-aware difference highlighting colors
GitHub link#
Source code is available in GitHub

