W3docs

Java Getters and Setters

Expose private fields safely in Java with getter and setter methods, and add validation in setters.

A getter is a method that returns the value of a field; a setter is a method that writes to one. Together they're the standard way to give controlled access to a private field. The previous chapter on encapsulation made the case for why — this chapter is about how, including the naming conventions, the validation patterns, and when you should skip them entirely.

The basic pattern

A field, a getter, and a setter:

public class User {
  private String email;

  public String getEmail()              { return email; }
  public void   setEmail(String email)  { this.email = email; }
}

By convention:

  • The getter is named get<FieldName> for everything except booleans.
  • For a boolean field, the getter is named is<FieldName> (isActive, hasPermission).
  • The setter is named set<FieldName> and takes one parameter of the field's type.
  • Both are public unless you have a reason otherwise.

These rules are also the JavaBeans convention — a lot of tooling (serialization libraries, template engines, IDE refactorings) relies on them and will simply fail to see fields whose accessors are named differently.

Validation in setters

A setter is your one chance to reject bad input before the field is written:

public class User {
  private String email;

  public void setEmail(String email) {
    if (email == null || !email.contains("@")) {
      throw new IllegalArgumentException("not a valid email: " + email);
    }
    this.email = email;
  }
}

A naked setter that does nothing but assign isn't much better than a public field. The point of putting a setter between callers and the field is to be able to put a check there. If you find yourself writing dozens of setters that all read like this.x = x;, ask whether the class really needs to be mutable at all — a constructor-set, getter-only object (an immutable class or a record) is often a better fit.

Computed and derived getters

A getter doesn't have to map one-to-one to a field. It can compute its return value:

public class Rectangle {
  private final double width, height;
  public Rectangle(double w, double h) { this.width = w; this.height = h; }

  public double getWidth()  { return width; }
  public double getHeight() { return height; }
  public double getArea()   { return width * height; }   // derived
}

To a caller, getArea() looks exactly like getWidth() — they're both just methods returning a double. The fact that one reads a stored value and the other computes it is an implementation detail you can change without anyone noticing.

Read-only getters

A getter without a setter exposes a field for reading but locks it for writing:

public class Order {
  private final long id;
  private final long createdAt;

  public Order(long id, long createdAt) {
    this.id        = id;
    this.createdAt = createdAt;
  }

  public long getId()        { return id; }
  public long getCreatedAt() { return createdAt; }
}

Outside code can ask "what's the ID?" but cannot change it. This is how immutable-after-construction fields work in practice.

Boolean naming: is, has, should

Boolean getters read more naturally with an is/has/can/should prefix:

public boolean isActive()        { return active; }
public boolean hasPermission()   { return permission != null; }
public boolean canRetry()        { return retries < maxRetries; }

Combined with the field name, the call site reads like an English sentence: if (user.isActive()) { ... }. The setter for these is just setActive(boolean), dropping the is.

Defensive copies (again)

If a getter would return a mutable internal object, return a copy or an unmodifiable view:

private final List<String> tags = new ArrayList<>();

public List<String> getTags() {
  return List.copyOf(tags);
}

Same on the way in for a setter:

public void setTags(List<String> tags) {
  this.tags.clear();
  this.tags.addAll(tags);    // copy in
}

Without these copies, the caller could mutate the internal tags list and bypass everything else the class does.

Modern Java: shorter accessors

Newer Java code (especially in libraries that aren't tied to JavaBeans tooling) often drops the get prefix for symmetry with how record accessors are named:

public class Point {
  private final int x, y;
  public Point(int x, int y) { this.x = x; this.y = y; }
  public int x() { return x; }
  public int y() { return y; }
}

If you're not bound by JavaBeans (no Hibernate, no Jackson default settings, no JSP), this style is fine — and matches what record Point(int x, int y) {} would auto-generate. Pick one style per codebase and apply it consistently.

Don't generate them by reflex

Modern IDEs will happily generate a getter and setter for every field with one keystroke. Resist the urge. Each generated pair is a design decision you're making:

  • A getter exposes a field — do you actually want callers reading it?
  • A setter exposes a write path — do you actually want callers changing it?
  • If both are unconditionally yes, why is the field private at all?

The right answer is often "neither" — replace setBalance(int) with deposit(int) and withdraw(int) that express the actual operations.

A worked example

java— editable, runs on the server

What's next

That wraps up the basics of a single class — how to declare it, how to control its members, how to expose just enough to the outside. The next chapter starts the second big OOP idea: inheritance, where one class builds on another instead of starting from scratch. Continue to Java inheritance.

Practice

Practice

Why is a setter usually preferable to making the field public, even if the setter does nothing but assign?