OVERVIEW
The forget keyword prevents further access to a variable, parameter, or field within a defined scope, attempts to access the variable in forbiden scope will result in a compile-time error.
This change makes variable and resource lifetimes explicit and compiler-enforced, improving code clarity and predictability.
-
Allows explicitly removing a variable from the active context (in terms of accessibility), which is currently:
- impossible for
finalvariables (only comments can be used), - impossible for method parameters (except assigning
nullto non-final references), - impossible for fields,
- cumbersome for local variables, requiring artificial blocks (extra lines and indentation).
- impossible for
- Makes it possible to explicitly declare that a variable should no longer be used or no longer represents valid data in the current scope.
-
Preserves code quality over time, avoiding degradation caused by
= nullassignments, comments-only conventions, or artificial scoping blocks.
The introduction of a new reserved keyword introduces potential source incompatibilities with existing codebases that define identifiers named forget.
Java currently provides only scope-based lifetime control (blocks and try-with-resources). It lacks a general, explicit, and compiler-enforced mechanism to terminate variable usability at an arbitrary point within an existing scope.
EXAMPLES
forget var;
// Variable is forgotten for the remainder of the current block or method (default behavior)
forget var : if;
// Variable is forgotten inside the entire if statement, including else and else-if branches
forget var : for;
// Variable is forgotten for the entire for-loop
forget var : while;
// Variable is forgotten for the entire while-loop
forget var : try;
// Variable is forgotten inside the try block (useful with resources)
forget var : label;
// Variable is forgotten inside the labeled block (any loop or code section)
forget var : static;
// Field is forgotten inside the static initialization block
forget var : method;
// Variable is forgotten for the remainder of the enclosing method (method-wide)
forget (var1, var2, ...);
// Specified variables are forgotten for the remainder of the current block
forget this.field;
// Specified field are forgotten for the remainder of the current block
forget (var1, var2, ...) { /* code */ };
// Specified variables are forgotten only inside the enclosed block
void handleRequest(String request, String token) {
if (!isTokenValid(token)) {
throw new SecurityException("Invalid token");
}
authorize(request, token);
forget token /* used & contains sensitive info */;
process(request);
logger.debug("token was: " + token); // Compile-time error: 'token' has been forgotten and cannot be used
}
public Product(String name) { // constructor
this.name = name.trim().intern();
forget name; // From now on, only use 'this.name'!
// other constructor commands...
if (isDuplicate(this.name)) { ... } // Always canonical, never raw input
if (isDuplicate(name)) { ... } // Compile-time ERROR!
}
// * Forces usage of the correctly prepared value (this.name) only.
// * Prevents code drift, maintenance bugs, or copy-paste errors that reference the raw parameter.
// * Makes the constructor safer: no risk of mismatches or inconsistent logic.
// * Reads as a contract: "from here on, don't touch the original argument!"
forget ClassName.field;
forget variable.field;
forget !(variable); // Limit allowed variables to ones that are directly specified
DETAILS
forget [ Identifier | ( IdentifierList ) ] [ : Scope | { block }];
IdentifierList:
Identifier {, Identifier}
Identifier:
[ VariableIdentifier | this.FieldIdentifier ]
The variable is not physically erased (except it may be if not a field); rather, it is protected from any further access after the forget statement. Additionally, retaining the variable in the scope (but inaccessible) prevents situations where a developer tries to create a new variable with the same name after removing the forget statement, thereby enforcing consistent usage and avoiding hidden bugs.
Testing the forget statement is equivalent to testing variable scope after exiting a block—the variable becomes inaccessible. For fields, forget enforces access control, ensuring the field cannot be used within the specified scope for the remainder of its block or method.
COMPATIBILITY
forget is already used as an identifier. There are no other compatibility impacts.
REFERENCES
PROBLEMS
- Backward Compatibility: Introducing forget as a new reserved keyword will cause compilation errors in existing code that already uses forget as an identifier (variable, method, class, etc).
- Tooling Lag: IDEs, static analysis tools, and debuggers must all be updated to handle the new keyword and its effects on variable visibility.
- Code Readability: Misuse or overuse of forget could make code harder to maintain or follow if not used judiciously, especially if variables are forgotten in non-obvious places.
- Teaching and Onboarding: This feature introduces a new concept that must be documented and taught to all developers, which can increase the learning curve for Java.
- Migration Complexity: Automatic migration of legacy code may be challenging, particularly for projects that rely on forget as an existing identifier or which have established conventions for variable lifetime.
- Interaction with Scoping and Shadowing: The detailed behavior when variables are forgotten, shadowed, or reintroduced in inner scopes may lead to confusion and subtle bugs if not carefully specified and implemented.
- Reflection and Debugging: While reflective APIs themselves are not impacted, developers may be surprised by the presence of variables at runtime (for debugging or reflection) that are "forgotten" in the source code.
- Consistency Across Language Features: Defining consistent behavior for forget in new contexts (e.g., lambdas, anonymous classes, record classes) may require extra specification effort.
- Edge Cases and Specification Complexity: Fully specifying the semantics of forget for all cases—including fields, parameters, captured variables in inner/nested classes, and interaction with try/catch/finally—may be complex.
- Unused Feature Risk: There is a risk that the forget keyword will see little real-world use, or will be misunderstood, if not supported and encouraged by frameworks or coding standards.
THE RIGHT AND THE WRONG WAY
For example, the request JDK-6189163: New language feature: undeclare operator made a similar suggestion. Unfortunately, much of the focus was on "destroying" variables as a matter of code complexity and maintenance. This focus is problematic for several reasons:
- The compiler can already release variables when they are no longer needed.
- Focusing on disposing bytes of data is really a bad step as it shouldn't be a concern in the first place.
- Focusing on variable usability in this way actually decreases maintainability, since both current and future modifications would require additional checks to unlock variables or identify where a variable has been "undeclared" in the middle of code. This complicates code changes unnecessarily.
At the same time, we should highlight that many language features are, to some extent, syntactic sugar from a usability perspective:
- Block-based local variable scoping
- Enhanced for Loop
- Final guard
- Generics
- Lambdas
- Records
- Switch Expressions
- Text Blocks
- Try-With-Resources
- ... and many more
On the other hand, when used properly, they can significantly reduce cognitive load and the time required for analysis, refactoring, and modification - and this path the change will aim to follow.
So we should focus on situations where forget is actually beneficial in the short or long term:
- When code is actively being developed -especially complex one - there are situations where removing unnecessary variables from the context is beneficial. It's entirely possible that in the final version these variables will be placed into proper scopes with no need of forget, but until then forget would serve an important purpose during development.
- When modifying long or complex code—especially if an issue arises from the incorrect reuse of a variable: We could safeguard the variable with a comment explaining its intended use and the change made. This targeted fix is preferable to an unannotated quick fix (which leaves confusion for future maintainers) or an extensive refactor, which could disrupt code history and introduce new errors.
- Writing security-sensitive code where resources need to be released in a specific order that cannot be enforced using classic block structures.
- Explicitly excluding class variables from the current method scope when ussage could be harmfull(for example in efficiency aspect), helping to prevent accidental usage and improve code clarity.
SIMPLIFIED VERSION
Simplified Solution:
- Instead of customizing scopes, forget could apply to the variable scope up to method/static scope.
- Caveat: It does NOT work for mutually exclusive scopes like parallel branches (if-else, switch-case). In those, you must restate forget in each branch.
void handleRequest(String request, String token) {
if (!isTokenValid(token)) {
throw new SecurityException("Invalid token");
}
if (forceLogOut()){
logOut(request, token);
forget token; // prevent usage in the rest of this block and after the if-else
...
} else {
authorize(request, token);
forget token; // 'forget' needs to be restated here
...
}
logger.debug("token was: " + token); // Compile-time error!
}
If more flexibility is needed, a more complex form like forget var : label; (for customization) can be introduced as an advanced feature.
Simplified version is added as alternative.
ReplyDelete