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
booleanfield, the getter is namedis<FieldName>(isActive,hasPermission). - The setter is named
set<FieldName>and takes one parameter of the field's type. - Both are
publicunless 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
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
Why is a setter usually preferable to making the field public, even if the setter does nothing but assign?