Skip to content

5 · 32 · 5 min

Deciding what the outside may touch

Every member of a class is either part of the promise you are making or an implementation detail you can change. Access modifiers are how you say which.

The levels

ModifierVisible to
privatethis class only
package private (no keyword, Java)the same package
protectedthis class and subclasses
publiceveryone

Python has no enforcement: a leading underscore is a convention meaning "not part of the interface", and a double underscore triggers name mangling rather than real privacy. The convention works because it is universally understood.

Start private

Default to private and widen only when there is a reason. Widening later is easy; narrowing is a breaking change for everyone using it.

This is the same argument as the API page in the software engineering course: anything visible is something someone will depend on, and you cannot see who.

protected is stronger than it looks

protected exposes a member to every subclass, including ones written by other people that you have never seen. It is effectively part of your public interface, permanently, and changing it breaks code you cannot find.

java
public class Widget {
    protected List<Item> items = new ArrayList<>();   // regretted later
}

Every subclass may now modify that list directly, so the class can no longer guarantee anything about it. Exposing behaviour through protected methods is defensible; exposing fields is rarely worth it.

A public field cannot be reconsidered

java
public class Circle {
    public double radius;          // no validation, ever
}

There is no place to reject a negative radius, no place to recompute a cached area, and no way to add one later without breaking every caller. A private field with accessors keeps that door open at almost no cost.

The exception is a value type whose fields genuinely are the whole of it and are immutable, which is what records and data classes are for.

Not a security mechanism

Reflection reaches private members in most languages. Access control expresses design intent and prevents accidents; it does not stop a determined caller, and it does nothing about someone who can run code in your process.