OOP Basics ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-08-04

JavaBeans Naming Conventions: Getters, Setters and boolean is

Take a working bean, change one field from boolean to Boolean so it can hold null, and the getter quietly stops being a getter:

public class Payment {
    private Boolean success;        // was: private boolean success;

    public Boolean isSuccess() {    // still compiles, still public
        return success;
    }
}
for (PropertyDescriptor pd : Introspector.getBeanInfo(Payment.class).getPropertyDescriptors()) {
    System.out.println(pd.getName());
}
// with primitive boolean: class, success
// with Boolean wrapper:   class          ← the "success" property is gone

Nothing warns you. The is prefix is legal only for the primitive boolean, and java.beans.Introspector checks the return type before it accepts the method. That is the kind of rule the JavaBeans naming conventions exist to pin down.

A JavaBean is an ordinary Java class written to a naming agreement so that tools can read and write its data without knowing anything about its internals. The data items it exposes are called properties, and each property is defined by a pair of methods: a getter that returns the value and a setter that assigns it. Fields stay private; the methods are public.

What is a JavaBean property

A property is not a field. It is a named piece of state that a bean exposes through accessor methods, and the JavaBeans specification recognises three flavours:

  • Read/write property — both a getter and a setter exist (getName() / setName(String)).
  • Read-only property — only a getter exists. Typical for values computed on the fly or set once in the constructor.
  • Write-only property — only a setter exists. Rare, but legal; a password field is the usual example.

Because a property is defined by methods, it does not need a matching field at all. This class exposes a perfectly valid fullName property that is stored nowhere:

public class Employee {
    private String first;
    private String last;

    public String getFullName() {      // read-only property "fullName"
        return first + " " + last;
    }
}

The reverse is also true: a private field with no accessors is invisible to a tool that works through the JavaBeans API. Frameworks that scan fields directly — Jackson with field visibility enabled, Hibernate with field access — are a separate mechanism and do not change these naming rules.

Getter and setter naming rules

The method name is built mechanically: take the property name, upper-case its first letter, and prepend the prefix.

Property type Getter prefix Setter prefix Property name Methods
Any type except boolean get set name getName() / setName(String)
boolean (primitive) is or get set printed isPrinted() / setPrinted(boolean)
Boolean (wrapper) get only set active getActive() / setActive(Boolean)
Array or collection get set roles getRoles() / setRoles(List<String>)

The signatures are just as strict as the names:

  1. A getter is public, takes no arguments, and returns the property type. It must not return void.
  2. A setter is public, returns void, and takes exactly one argument of the property type.
  3. The setter argument type must match the getter return type, or the two methods describe different properties.
public int getAge() {
    return age;
}

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

The this. qualifier in the setter is not optional style. The parameter age shadows the field age, so age = age; assigns the parameter to itself and leaves the field at 0. It compiles cleanly — only your IDE or a static analyser will flag it.

boolean getters: is vs get

This is where the convention bites most often. The JavaBeans specification allows isXxx() as an alternative to getXxx() only when the property type is the primitive boolean. For the Boolean wrapper, getXxx() is the only form the standard introspector accepts.

private boolean enabled;
public boolean isEnabled() { return enabled; }     // valid: primitive boolean
public boolean getEnabled() { return enabled; }    // also valid, just less idiomatic

private Boolean archived;
public Boolean isArchived() { return archived; }   // NOT a getter for Introspector
public Boolean getArchived() { return archived; }  // correct form for the wrapper

If a class declares both isEnabled() and getEnabled(), they describe the same property enabled, and Introspector prefers the is variant as the read method. Declaring both is redundant — pick one, and prefer isEnabled() for primitives because that is what IDEs generate and what other developers expect.

Not every library reads the rule the same way

Tools built on java.beans.Introspector — JSP and JSF expression language, JavaFX, Spring’s BeanWrapper, most bean-mapping utilities — reject isArchived() when it returns Boolean. Jackson is more forgiving and accepts an is getter for both boolean and Boolean. So the same class can serialise to JSON correctly and still resolve to nothing in ${order.archived}. Write getArchived() for wrappers and the ambiguity disappears.

How the property name is derived

The introspector strips the prefix and then decapitalises what remains. The rule lives in Introspector.decapitalize(): lower-case the first character, unless the first two characters are both upper case, in which case the name is left untouched.

Method After the prefix Property name Why
getName() Name name First letter lower-cased
isSuccess() Success success The is prefix is dropped like get; the property is not called isSuccess
getURL() URL URL Two leading capitals, so the name is kept as is
getS1_var() S1_var s1_var Second character is not a capital letter, so decapitalisation applies
gets1_var() s1_var s1_var Already lower-case, nothing to change — the same property as above

Two consequences worth memorising. First, isSuccess() defines a property named success, so the JSON key, the EL expression and the JSF binding are all success, never isSuccess. Second, getS1_var() and gets1_var() collapse to the same property name, which is exactly why a lower-case letter straight after the prefix is worth avoiding even though the introspector tolerates it.

The field name is irrelevant

A bean’s property list comes from its methods, not its fields. private String s; exposed through getName() / setName(String) is a property called name. Conversely, a field named sName whose accessors were generated as getsName() yields the property sName, which is almost never what the mapping configuration expects. Name fields the way you want the properties to be named.

What makes a class a JavaBean

Beyond the accessor names, the specification asks for a handful of structural guarantees so that a tool can instantiate and populate the object generically.

Requirement Why it exists How strict it is in practice
public class The framework has to load and reference the type Mandatory
public no-argument constructor The object is created reflectively, before any values are known Mandatory for Hibernate and for default Jackson deserialisation
private fields State can only change through methods you control Convention, but there is no reason to break it
Accessors following the naming rules They are what defines the property set Mandatory
implements Serializable Lets the bean’s state be written to a stream Required by the spec, rarely relied on in modern web code

Almost the whole Java ecosystem leans on this agreement: Jackson and Gson for JSON, JPA and Hibernate for object-relational mapping, Spring for setter injection and configuration binding, JSP and JSF expression language for ${person.fullName}, JavaFX for property binding, and every bean-mapping library in between. The formal source is the JavaBeans specification published by Oracle; the runtime implementation is java.beans.Introspector.

Example: the Person bean

A complete bean with three properties. Note isRetired(): the field is a primitive boolean, so the is prefix is allowed.

import java.io.Serializable;

public class Person implements Serializable {
    private String fullName;
    private int age;
    private boolean retired;

    public Person() {
    }

    public Person(String fullName, int age, boolean retired) {
        this.fullName = fullName;
        this.age = age;
        this.retired = retired;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public int getAge() {
        return age;
    }

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

    public boolean isRetired() {
        return retired;
    }

    public void setRetired(boolean retired) {
        this.retired = retired;
    }
}

You can ask the JDK what it sees instead of guessing:

import java.beans.Introspector;
import java.beans.PropertyDescriptor;

public class PersonExample {
    public static void main(String[] args) throws Exception {
        Person person = new Person();
        person.setFullName("John Smith");
        person.setAge(56);
        person.setRetired(false);

        System.out.println(person.getFullName() + ", " + person.getAge()
                + ", retired: " + person.isRetired());

        for (PropertyDescriptor pd : Introspector.getBeanInfo(Person.class, Object.class)
                .getPropertyDescriptors()) {
            System.out.println(pd.getName() + " -> " + pd.getPropertyType().getSimpleName());
        }
    }
}
John Smith, 56, retired: false
age -> int
fullName -> String
retired -> boolean

The property is retired, not isRetired — the prefix never survives into the name.

Why not just use public fields

Accessors add lines of code, so the objection is fair. The answer shows up as soon as two fields depend on each other:

public class CircleUnsafe {
    public double radius;
    public double diam;
}

CircleUnsafe c = new CircleUnsafe();
c.diam = 25;
c.radius = 10;   // compiles fine, object is now geometrically impossible

Java checks types, not meaning, so nothing objects. The bad state travels through the program and surfaces three modules later in a report nobody can explain. Route the changes through setters and the class can defend its own invariant:

public class Circle {
    private double radius;
    private double diam;

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        if (radius < 0) {
            throw new IllegalArgumentException("Radius cannot be negative: " + radius);
        }
        this.radius = radius;
        this.diam = radius * 2;
    }

    public double getDiam() {
        return diam;
    }

    public void setDiam(double diam) {
        if (diam < 0) {
            throw new IllegalArgumentException("Diameter cannot be negative: " + diam);
        }
        this.diam = diam;
        this.radius = diam / 2;
    }
}
Circle circle = new Circle();
circle.setDiam(25);
System.out.println(circle.getDiam());     // 25.0
System.out.println(circle.getRadius());   // 12.5 - consistent

That is the real job of a setter: not "assign a value" but "keep the object valid". A getter can carry logic too — lazy initialisation, formatting, a defensive copy — as long as it still returns the property it claims to return.

Watch the arithmetic inside the setter

The fields above are double on purpose. With int, setDiam(25) would compute 25 / 2 = 12 by integer division, leaving a radius of 12 next to a diameter of 25 — the invariant is broken again, only more quietly. A setter protects the object exactly as far as the arithmetic inside it is correct.

If a getter has to return something other than the raw property value — a masked password, for instance — give it a different name such as getMaskedPassword(). Naming it getPassword() makes every JavaBeans-aware tool believe the masked string is the property, and Jackson will happily serialise "m*****" while Hibernate writes it to the database.

Event listener naming conventions

The JavaBeans specification also covers events: a bean can notify interested objects, called listeners, when something happens. The registration methods follow their own naming rules:

  • A method that registers a listener uses the prefix add followed by the listener type: addActionListener().
  • A method that unregisters one uses the prefix remove followed by the same type: removeActionListener().
  • The listener itself is passed as the single argument to the method.
  • The listener type name ends with the word Listener.
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public class Order {
    private final PropertyChangeSupport support = new PropertyChangeSupport(this);
    private String status;

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        support.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        support.removePropertyChangeListener(listener);
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        String old = this.status;
        this.status = status;
        support.firePropertyChange("status", old, status);
    }
}

A property whose setter fires such an event is called a bound property. A property whose listeners may veto the change by throwing PropertyVetoException is a constrained property, registered through addVetoableChangeListener(). Both patterns are what Swing and JavaFX components are built on.

IDE generation and Lombok

Nobody types accessors by hand. IntelliJ IDEA generates them with Alt + InsertGetter and Setter, Eclipse with SourceGenerate Getters and Setters. Both apply the prefix rules correctly, including is for primitive boolean.

The alternative is Lombok, which generates the methods at compile time from annotations:

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class Person {
    private String fullName;
    private int age;
    private boolean retired;
}
Annotation What it generates When to use it
@Getter / @Setter Accessors for every field, or for a single field when placed on it Default choice: you keep control over what is exposed
@Data @Getter, @Setter, toString(), equals(), hashCode() and a required-args constructor Plain DTOs. Avoid on JPA entities: the generated equals / hashCode misbehave with lazy associations
@Value An immutable class: final fields, getters only Value objects on Java versions without records

The bytecode contains real getFullName(), setFullName(String) and isRetired() methods, so introspection works exactly as if you had written them.

Records vs JavaBeans

Since Java 16, a class whose only job is to carry data can be a record:

public record Person(String fullName, int age, boolean retired) {
}

Person person = new Person("John Smith", 56, false);
System.out.println(person.fullName());   // not getFullName()
System.out.println(person.retired());    // not isRetired()
Aspect JavaBean Record
Accessor name getFullName(), isRetired() fullName(), retired()
Mutability Mutable through setters Immutable, no setters at all
No-arg constructor Required Impossible
Inheritance Can be extended final by definition
Best fit JPA entities, form backing objects, mutable state DTOs, API responses, map keys, query results

Strictly speaking a record is not a JavaBean: no setters, no no-arg constructor, and accessor names that ignore the get prefix. Modern libraries handle records natively — Jackson has supported them since 2.12 — but Hibernate still needs a mutable class with a default constructor for an entity. The design rationale is in JEP 395.

Where developers get tripped up

  1. Returning the live collection. getRoles() handing back the internal List undoes the encapsulation completely: the caller can run person.getRoles().clear(). Return List.copyOf(roles) or Collections.unmodifiableList(roles).
  2. Getter and setter types that do not match. public long getId() next to public void setId(int id) does not form a read/write property; the introspector reports a read-only id and drops the setter, so values silently never get written.
  3. Assuming the field name drives the property. It never does. Rename the field freely; rename an accessor and you have renamed a property, together with its JSON key, its column mapping and every EL expression pointing at it.
  4. Losing the no-arg constructor. Adding any constructor removes the implicit default one, and Hibernate fails with InstantiationException while Jackson throws InvalidDefinitionException. Declare it explicitly.
  5. Calling a setter from a constructor. If a subclass overrides that setter, it runs before the subclass fields are initialised and sees them at their default values. Assign fields directly inside constructors.
  6. Accessors on every single field by reflex. A bare get/set pair over a field is no better than a public field, just longer. Write the ones the outside world genuinely needs, and let the rest stay private.

Frequently asked questions

What property name does isSuccess() define?

The property is called success. The introspector removes the is prefix exactly as it removes get, then decapitalises the rest, so the JSON key, the JSF binding and the expression ${payment.success} all use success. There is no way to get a property literally named isSuccess out of an is getter; you would need a method called getIsSuccess(), which is worth avoiding.

Can a getter and a setter use different types for the same property?

No. The setter parameter type has to equal the getter return type. If they differ, java.beans.Introspector does not pair them into one read/write property: you end up with a read-only property and a setter that no framework will ever call. This is a common cause of a field that loads from the database correctly but never saves.

Does the field name have to match the getter name?

Not technically. A bean can expose a property with no backing field at all, and a field can be named differently from the property its accessors define. In practice, keeping them aligned is what everyone expects, because mismatched names make configuration files, log output and debugging sessions harder to follow.

How are indexed properties named?

An indexed property adds a second accessor pair that takes an index: getRoles() and setRoles(String[]) for the whole array, plus getRoles(int index) and setRoles(int index, String role) for one element. The introspector reports it as an IndexedPropertyDescriptor. Indexed properties come from the original component-tool era and are little used in modern code, where a plain List property is preferred.

Do records follow JavaBeans naming conventions?

No. A record generates accessors named after the component, such as fullName() instead of getFullName(), and it has neither setters nor a no-argument constructor. Libraries updated for modern Java read records directly, but anything that relies on java.beans.Introspector will find no properties on a record. Use records for immutable data carriers and a classic bean where a framework demands the convention.

Comments

Please log in or register to have a possibility to add comment.