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
publicorprivate. - 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/privatethemselves. - Block scope — variables declared inside any
{}block narrower than a whole method (a loop, anif, 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.fieldNameto reach the instance variable explicitly. - Confusing
finalwith true immutability.finalonly 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.fieldNameis how to reach the real instance variable when that happens. finalallows 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 likePose2dsafe 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