dev only

Scope and Access

What Scope Means

A variable's scope is where it's accessible — where it "lives and exists." Scope is determined by the nearest enclosing pair of curly braces {} around wherever the variable was declared. Trying to use a variable outside its scope isn't just bad practice — the variable genuinely doesn't exist there, and the code won't compile.

Three Levels of Scope

public class Intake
{
    private boolean hasGamePiece;             // CLASS scope — visible to every method in Intake

    public void setState(boolean detected)    // `detected` is a parameter — METHOD scope
    {
        for (int i = 0; i < 3; i++)           // `i` is a loop variable — BLOCK scope
        {
            boolean sample = detected;        // BLOCK scope — only exists inside this loop's braces
        }
        hasGamePiece = detected;              // fine: class-scope field, reachable anywhere in the class
    }
}
  • Class scope — instance variables (fields), visible to every method in the class, regardless of whether they're public or private.
  • Method scope — local variables and parameters, declared inside a method or constructor; they exist only for that one method call, and can never be declared public/private themselves.
  • Block scope — variables declared inside any {} block narrower than a whole method (a loop, an if, etc.); they stop existing the instant that block ends.

Local Variables Only Live Where They're Declared

public String getStatus()
{
    for (int i = 0; i < 3; i++)
    {
        int sample = i;
    }
    System.out.println(sample); // COMPILE ERROR — `sample` only existed inside the loop's braces
    return "done";
}

Parameters follow the same rule: a constructor's parameter only exists for that constructor call, and isn't reachable from any other method in the class — including toString() or any other method that seems related.

Shadowing: When a Local Name Matches a Field Name

If a local variable (or parameter) shares a name with an instance variable, the local one wins inside that method — this is called shadowing:

public class Intake
{
    private String name;

    @Override
    public String toString()
    {
        String name = "unknown";  // this local variable shadows the instance variable `name`
        return name;              // returns "unknown", NOT the instance variable's real value
    }
}

Ch.8 already introduced the fix for this exact situation inside constructors — this.name explicitly reaches the instance variable even when a same-named local variable or parameter is shadowing it.

Connecting Scope to Encapsulation

Encapsulation (Ch.7) is really scope applied deliberately: marking a field private doesn't change its class scope (every method in the class can still see it) — it restricts outside code from reaching it directly, forcing access through public getters/setters instead. Scope and access modifiers (public/private) are two separate but related ideas: scope is about where a variable exists at all; access modifiers are about who outside the class is allowed to touch it.

The final Keyword

A field, parameter, or local variable marked final can be assigned exactly once — any attempt to reassign it afterward is a compile error. This matters for anything that genuinely should never change after it's set:

private final TalonFX motor = new TalonFX(1); // this reference can never be reassigned to a different motor

public double getVelocity()
{
    return motor.getVelocity().getValue();
}

final on an object reference only locks the reference itself — it doesn't make the object's own internal state unchangeable (motor can't be pointed at a different TalonFX, but the real TalonFX object it points to can still have its own state change via its own methods). True immutability — an object whose internal state can never change at all once constructed — is a stronger guarantee, and it's exactly why immutable classes like WPILib's Pose2d are thread-safe and predictable: nothing can ever be halfway through changing one, because nothing can change it at all.

Common Pitfalls

  • Trying to use a loop or block-scoped variable after its block ends. It stops existing the instant the enclosing {} closes — declare it at method scope instead if it needs to survive past the block.
  • Assuming a shadowed local variable is the instance variable. Inside a method with a same-named local variable/parameter, the local one always wins — use this.fieldName to reach the instance variable explicitly.
  • Confusing final with true immutability. final only prevents reassigning a reference — the object it refers to can still have its own internal state changed, unless that object's own class was specifically designed to be immutable.

Key Takeaways

  • A variable's scope — class, method, or block — is set by the nearest enclosing {} around its declaration; using it outside that scope doesn't compile.
  • Local variables and parameters exist only for their method/constructor call; instance variables exist for the whole object, reachable from every method regardless of access modifier.
  • A local variable or parameter sharing a name with an instance variable shadows it inside that method — this.fieldName is how to reach the real instance variable when that happens.
  • final allows exactly one assignment to a variable — for object references, it locks the reference, not necessarily the referenced object's internal state.
  • True immutability (an object whose state can never change after construction) is a stronger, separate guarantee from final — and it's what makes classes like Pose2d safe to share and reason about.

Derived from csawesome-2026: unit-3-class-creation/3.08-scope-and-access.md (CSA 3.8) Deck context: mechacoder-test/src/lessons/java-2.js, slide 9 ("Encapsulation & Final") — final TalonFX/Pose2d example