How do I access private fields using getters and setters in Java?

You can use getter and setter methods to read and update private fields. A getter returns a field’s value, while a setter changes it.

These methods let a class control how its data is accessed and updated.

In this example, you will create a Book class with a private title field. A setter will prevent the title from being null or blank.

Source code

public class GetterSetterExample {
    void main() {
        Book book = new Book("Learning Java");

        IO.println("Original title: " + book.getTitle());

        book.setTitle("Exploring Java");

        IO.println("Updated title: " + book.getTitle());
    }
}

class Book {
    private String title;

    Book(String title) {
        this.title = validateTitle(title);
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = validateTitle(title);
    }

    private static String validateTitle(String title) {
        if (title == null || title.isBlank()) {
            throw new IllegalArgumentException(
                    "Title must not be null or blank.");
        }

        return title;
    }
}

Explanation

The title field is private:

private String title;

Code in GetterSetterExample cannot access this field directly. For example, the following statement would cause a compilation error:

book.title = "Exploring Java";

Instead, the class provides methods for reading and updating the value.

Reading a value with a getter

The getTitle() method returns the current title:

public String getTitle() {
    return title;
}

The calling code uses it like this:

IO.println("Original title: " + book.getTitle());

A getter commonly uses the prefix get followed by the field name, such as getTitle().

Updating a value with a setter

The setTitle() method accepts a new title:

public void setTitle(String title) {
    this.title = validateTitle(title);
}

It validates the input before assigning it to the field. If validation fails, the assignment does not happen, and the existing title stays unchanged.

The calling code updates the title using:

book.setTitle("Exploring Java");

Validating the title

Both the constructor and setter call the same validation method:

private static String validateTitle(String title) {
    if (title == null || title.isBlank()) {
        throw new IllegalArgumentException(
                "Title must not be null or blank.");
    }

    return title;
}

The method rejects null, empty strings, and strings containing only whitespace.

The || operator evaluates the second condition only when the first condition is false. If title is null, Java skips title.isBlank(), avoiding a NullPointerException.

The validation method is private because it is an implementation detail. It is static because it does not use any object’s fields.

Run the example

Compile and run the file:

javac GetterSetterExample.java
java GetterSetterExample

Output

Original title: Learning Java
Updated title: Exploring Java

Private fields with controlled access are an example of encapsulation. Add setters only for values that callers should be allowed to change. A field that should remain fixed can be initialized through a constructor and exposed through a getter.

How do I write custom getters and setters in Kotlin properties?

In Kotlin, properties can have custom getters and setters by defining get() and/or set(value) directly under the property.

var propertyName: Type = initialValue
    get() {
        return field
    }
    set(value) {
        field = value
    }

field is the backing field automatically generated by Kotlin when needed.

Custom getter

class Person(
    val firstName: String,
    val lastName: String
) {
    val fullName: String
        get() = "$firstName $lastName"
}

Usage:

val person = Person("Ada", "Lovelace")
println(person.fullName) // Ada Lovelace

Here, fullName does not store a value. It is computed every time it is accessed.

Custom setter

class User {
    var age: Int = 0
        set(value) {
            field = if (value >= 0) value else 0
        }
}

Usage:

val user = User()
user.age = -5
println(user.age) // 0

The setter validates the assigned value before storing it.

Custom getter and setter together

class Temperature {
    var celsius: Double = 0.0
        get() = field
        set(value) {
            field = value.coerceAtLeast(-273.15)
        }

    var fahrenheit: Double
        get() = celsius * 9 / 5 + 32
        set(value) {
            celsius = (value - 32) * 5 / 9
        }
}

Usage:

val temperature = Temperature()

temperature.celsius = 100.0
println(temperature.fahrenheit) // 212.0

temperature.fahrenheit = 32.0
println(temperature.celsius) // 0.0

Important rules

  • val properties can only have a custom getter, not a setter.
  • var properties can have both a getter and a setter.
  • Use field inside accessors when you want to refer to the property’s backing field.
  • Do not write propertyName = value inside its own setter, because that recursively calls the setter.

For example, avoid this:

var name: String = ""
    set(value) {
        name = value // recursive setter call
    }

Use this instead:

var name: String = ""
    set(value) {
        field = value
    }

Example with validation

class Product {
    var price: Double = 0.0
        set(value) {
            require(value >= 0) { "Price cannot be negative" }
            field = value
        }
}
val product = Product()
product.price = 19.99
println(product.price)

// product.price = -1.0
// Throws IllegalArgumentException: Price cannot be negative

So the basic pattern is:

var myProperty: String = ""
    get() = field
    set(value) {
        field = value.trim()
    }